1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master.procedure;
20
21 import java.io.InputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.hbase.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.TableName;
33 import org.apache.hadoop.hbase.TableNotDisabledException;
34 import org.apache.hadoop.hbase.TableNotFoundException;
35 import org.apache.hadoop.hbase.HRegionInfo;
36 import org.apache.hadoop.hbase.HTableDescriptor;
37 import org.apache.hadoop.hbase.exceptions.HBaseException;
38 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
39 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
40 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
41 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.TruncateTableState;
42 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
43 import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
44 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
45 import org.apache.hadoop.security.UserGroupInformation;
46
47 @InterfaceAudience.Private
48 public class TruncateTableProcedure
49 extends StateMachineProcedure<MasterProcedureEnv, TruncateTableState>
50 implements TableProcedureInterface {
51 private static final Log LOG = LogFactory.getLog(TruncateTableProcedure.class);
52
53 private boolean preserveSplits;
54 private List<HRegionInfo> regions;
55 private UserGroupInformation user;
56 private HTableDescriptor hTableDescriptor;
57 private TableName tableName;
58
59 public TruncateTableProcedure() {
60
61 }
62
63 public TruncateTableProcedure(final MasterProcedureEnv env, final TableName tableName,
64 boolean preserveSplits) throws IOException {
65 this.tableName = tableName;
66 this.preserveSplits = preserveSplits;
67 this.user = env.getRequestUser().getUGI();
68 this.setOwner(this.user.getShortUserName());
69 }
70
71 @Override
72 protected Flow executeFromState(final MasterProcedureEnv env, TruncateTableState state) {
73 if (LOG.isTraceEnabled()) {
74 LOG.trace(this + " execute state=" + state);
75 }
76 try {
77 switch (state) {
78 case TRUNCATE_TABLE_PRE_OPERATION:
79
80 if (!prepareTruncate(env)) {
81 assert isFailed() : "the truncate should have an exception here";
82 return Flow.NO_MORE_STATE;
83 }
84
85
86 LOG.debug("waiting for '" + getTableName() + "' regions in transition");
87 regions = ProcedureSyncWait.getRegionsFromMeta(env, getTableName());
88 assert regions != null && !regions.isEmpty() : "unexpected 0 regions";
89 ProcedureSyncWait.waitRegionInTransition(env, regions);
90
91
92 preTruncate(env);
93
94 setNextState(TruncateTableState.TRUNCATE_TABLE_REMOVE_FROM_META);
95 break;
96 case TRUNCATE_TABLE_REMOVE_FROM_META:
97 hTableDescriptor = env.getMasterServices().getTableDescriptors().get(tableName);
98 DeleteTableProcedure.deleteFromMeta(env, getTableName(), regions);
99 DeleteTableProcedure.deleteAssignmentState(env, getTableName());
100 setNextState(TruncateTableState.TRUNCATE_TABLE_CLEAR_FS_LAYOUT);
101 break;
102 case TRUNCATE_TABLE_CLEAR_FS_LAYOUT:
103 DeleteTableProcedure.deleteFromFs(env, getTableName(), regions, true);
104 if (!preserveSplits) {
105
106 regions = Arrays.asList(ModifyRegionUtils.createHRegionInfos(hTableDescriptor, null));
107 }
108 setNextState(TruncateTableState.TRUNCATE_TABLE_CREATE_FS_LAYOUT);
109 break;
110 case TRUNCATE_TABLE_CREATE_FS_LAYOUT:
111 regions = CreateTableProcedure.createFsLayout(env, hTableDescriptor, regions);
112 CreateTableProcedure.updateTableDescCache(env, getTableName());
113 setNextState(TruncateTableState.TRUNCATE_TABLE_ADD_TO_META);
114 break;
115 case TRUNCATE_TABLE_ADD_TO_META:
116 regions = CreateTableProcedure.addTableToMeta(env, hTableDescriptor, regions);
117 setNextState(TruncateTableState.TRUNCATE_TABLE_ASSIGN_REGIONS);
118 break;
119 case TRUNCATE_TABLE_ASSIGN_REGIONS:
120 CreateTableProcedure.assignRegions(env, getTableName(), regions);
121 setNextState(TruncateTableState.TRUNCATE_TABLE_POST_OPERATION);
122 hTableDescriptor = null;
123 regions = null;
124 break;
125 case TRUNCATE_TABLE_POST_OPERATION:
126 postTruncate(env);
127 LOG.debug("truncate '" + getTableName() + "' completed");
128 return Flow.NO_MORE_STATE;
129 default:
130 throw new UnsupportedOperationException("unhandled state=" + state);
131 }
132 } catch (HBaseException|IOException e) {
133 LOG.warn("Retriable error trying to truncate table=" + getTableName() + " state=" + state, e);
134 } catch (InterruptedException e) {
135
136 LOG.warn("Interrupted trying to truncate table=" + getTableName() + " state=" + state, e);
137 }
138 return Flow.HAS_MORE_STATE;
139 }
140
141 @Override
142 protected void rollbackState(final MasterProcedureEnv env, final TruncateTableState state) {
143 if (state == TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION) {
144
145
146 return;
147 }
148
149
150 throw new UnsupportedOperationException("unhandled state=" + state);
151 }
152
153 @Override
154 protected TruncateTableState getState(final int stateId) {
155 return TruncateTableState.valueOf(stateId);
156 }
157
158 @Override
159 protected int getStateId(final TruncateTableState state) {
160 return state.getNumber();
161 }
162
163 @Override
164 protected TruncateTableState getInitialState() {
165 return TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION;
166 }
167
168 @Override
169 public TableName getTableName() {
170 return tableName;
171 }
172
173 @Override
174 public TableOperationType getTableOperationType() {
175 return TableOperationType.EDIT;
176 }
177
178 @Override
179 public boolean abort(final MasterProcedureEnv env) {
180
181 return false;
182 }
183
184 @Override
185 protected boolean acquireLock(final MasterProcedureEnv env) {
186 if (!env.isInitialized()) return false;
187 return env.getProcedureQueue().tryAcquireTableWrite(getTableName(), "truncate table");
188 }
189
190 @Override
191 protected void releaseLock(final MasterProcedureEnv env) {
192 env.getProcedureQueue().releaseTableWrite(getTableName());
193 }
194
195 @Override
196 public void toStringClassDetails(StringBuilder sb) {
197 sb.append(getClass().getSimpleName());
198 sb.append(" (table=");
199 sb.append(getTableName());
200 sb.append(" preserveSplits=");
201 sb.append(preserveSplits);
202 sb.append(")");
203 }
204
205 @Override
206 public void serializeStateData(final OutputStream stream) throws IOException {
207 super.serializeStateData(stream);
208
209 MasterProcedureProtos.TruncateTableStateData.Builder state =
210 MasterProcedureProtos.TruncateTableStateData.newBuilder()
211 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(this.user))
212 .setPreserveSplits(preserveSplits);
213 if (hTableDescriptor != null) {
214 state.setTableSchema(hTableDescriptor.convert());
215 } else {
216 state.setTableName(ProtobufUtil.toProtoTableName(tableName));
217 }
218 if (regions != null) {
219 for (HRegionInfo hri: regions) {
220 state.addRegionInfo(HRegionInfo.convert(hri));
221 }
222 }
223 state.build().writeDelimitedTo(stream);
224 }
225
226 @Override
227 public void deserializeStateData(final InputStream stream) throws IOException {
228 super.deserializeStateData(stream);
229
230 MasterProcedureProtos.TruncateTableStateData state =
231 MasterProcedureProtos.TruncateTableStateData.parseDelimitedFrom(stream);
232 user = MasterProcedureUtil.toUserInfo(state.getUserInfo());
233 if (state.hasTableSchema()) {
234 hTableDescriptor = HTableDescriptor.convert(state.getTableSchema());
235 tableName = hTableDescriptor.getTableName();
236 } else {
237 tableName = ProtobufUtil.toTableName(state.getTableName());
238 }
239 preserveSplits = state.getPreserveSplits();
240 if (state.getRegionInfoCount() == 0) {
241 regions = null;
242 } else {
243 regions = new ArrayList<HRegionInfo>(state.getRegionInfoCount());
244 for (HBaseProtos.RegionInfo hri: state.getRegionInfoList()) {
245 regions.add(HRegionInfo.convert(hri));
246 }
247 }
248 }
249
250 private boolean prepareTruncate(final MasterProcedureEnv env) throws IOException {
251 try {
252 env.getMasterServices().checkTableModifiable(getTableName());
253 } catch (TableNotFoundException|TableNotDisabledException e) {
254 setFailure("master-truncate-table", e);
255 return false;
256 }
257 return true;
258 }
259
260 private boolean preTruncate(final MasterProcedureEnv env)
261 throws IOException, InterruptedException {
262 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
263 if (cpHost != null) {
264 final TableName tableName = getTableName();
265 user.doAs(new PrivilegedExceptionAction<Void>() {
266 @Override
267 public Void run() throws Exception {
268 cpHost.preTruncateTableHandler(tableName);
269 return null;
270 }
271 });
272 }
273 return true;
274 }
275
276 private void postTruncate(final MasterProcedureEnv env)
277 throws IOException, InterruptedException {
278 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
279 if (cpHost != null) {
280 final TableName tableName = getTableName();
281 user.doAs(new PrivilegedExceptionAction<Void>() {
282 @Override
283 public Void run() throws Exception {
284 cpHost.postTruncateTableHandler(tableName);
285 return null;
286 }
287 });
288 }
289 }
290 }