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) {
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 } else {
108 regions = recreateRegionInfo(regions);
109 }
110 setNextState(TruncateTableState.TRUNCATE_TABLE_CREATE_FS_LAYOUT);
111 break;
112 case TRUNCATE_TABLE_CREATE_FS_LAYOUT:
113 regions = CreateTableProcedure.createFsLayout(env, hTableDescriptor, regions);
114 CreateTableProcedure.updateTableDescCache(env, getTableName());
115 setNextState(TruncateTableState.TRUNCATE_TABLE_ADD_TO_META);
116 break;
117 case TRUNCATE_TABLE_ADD_TO_META:
118 regions = CreateTableProcedure.addTableToMeta(env, hTableDescriptor, regions);
119 setNextState(TruncateTableState.TRUNCATE_TABLE_ASSIGN_REGIONS);
120 break;
121 case TRUNCATE_TABLE_ASSIGN_REGIONS:
122 CreateTableProcedure.assignRegions(env, getTableName(), regions);
123 setNextState(TruncateTableState.TRUNCATE_TABLE_POST_OPERATION);
124 hTableDescriptor = null;
125 regions = null;
126 break;
127 case TRUNCATE_TABLE_POST_OPERATION:
128 postTruncate(env);
129 LOG.debug("truncate '" + getTableName() + "' completed");
130 return Flow.NO_MORE_STATE;
131 default:
132 throw new UnsupportedOperationException("unhandled state=" + state);
133 }
134 } catch (HBaseException|IOException e) {
135 LOG.warn("Retriable error trying to truncate table=" + getTableName() + " state=" + state, e);
136 } catch (InterruptedException e) {
137
138 LOG.warn("Interrupted trying to truncate table=" + getTableName() + " state=" + state, e);
139 }
140 return Flow.HAS_MORE_STATE;
141 }
142
143 @Override
144 protected void rollbackState(final MasterProcedureEnv env, final TruncateTableState state) {
145 if (state == TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION) {
146
147
148 return;
149 }
150
151
152 throw new UnsupportedOperationException("unhandled state=" + state);
153 }
154
155 @Override
156 protected TruncateTableState getState(final int stateId) {
157 return TruncateTableState.valueOf(stateId);
158 }
159
160 @Override
161 protected int getStateId(final TruncateTableState state) {
162 return state.getNumber();
163 }
164
165 @Override
166 protected TruncateTableState getInitialState() {
167 return TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION;
168 }
169
170 @Override
171 public TableName getTableName() {
172 return tableName;
173 }
174
175 @Override
176 public TableOperationType getTableOperationType() {
177 return TableOperationType.EDIT;
178 }
179
180 @Override
181 public boolean abort(final MasterProcedureEnv env) {
182
183 return false;
184 }
185
186 @Override
187 protected boolean acquireLock(final MasterProcedureEnv env) {
188 if (!env.isInitialized()) return false;
189 return env.getProcedureQueue().tryAcquireTableWrite(getTableName(), "truncate table");
190 }
191
192 @Override
193 protected void releaseLock(final MasterProcedureEnv env) {
194 env.getProcedureQueue().releaseTableWrite(getTableName());
195 }
196
197 @Override
198 public void toStringClassDetails(StringBuilder sb) {
199 sb.append(getClass().getSimpleName());
200 sb.append(" (table=");
201 sb.append(getTableName());
202 sb.append(" preserveSplits=");
203 sb.append(preserveSplits);
204 sb.append(")");
205 }
206
207 @Override
208 public void serializeStateData(final OutputStream stream) throws IOException {
209 super.serializeStateData(stream);
210
211 MasterProcedureProtos.TruncateTableStateData.Builder state =
212 MasterProcedureProtos.TruncateTableStateData.newBuilder()
213 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(this.user))
214 .setPreserveSplits(preserveSplits);
215 if (hTableDescriptor != null) {
216 state.setTableSchema(hTableDescriptor.convert());
217 } else {
218 state.setTableName(ProtobufUtil.toProtoTableName(tableName));
219 }
220 if (regions != null) {
221 for (HRegionInfo hri: regions) {
222 state.addRegionInfo(HRegionInfo.convert(hri));
223 }
224 }
225 state.build().writeDelimitedTo(stream);
226 }
227
228 @Override
229 public void deserializeStateData(final InputStream stream) throws IOException {
230 super.deserializeStateData(stream);
231
232 MasterProcedureProtos.TruncateTableStateData state =
233 MasterProcedureProtos.TruncateTableStateData.parseDelimitedFrom(stream);
234 user = MasterProcedureUtil.toUserInfo(state.getUserInfo());
235 if (state.hasTableSchema()) {
236 hTableDescriptor = HTableDescriptor.convert(state.getTableSchema());
237 tableName = hTableDescriptor.getTableName();
238 } else {
239 tableName = ProtobufUtil.toTableName(state.getTableName());
240 }
241 preserveSplits = state.getPreserveSplits();
242 if (state.getRegionInfoCount() == 0) {
243 regions = null;
244 } else {
245 regions = new ArrayList<HRegionInfo>(state.getRegionInfoCount());
246 for (HBaseProtos.RegionInfo hri: state.getRegionInfoList()) {
247 regions.add(HRegionInfo.convert(hri));
248 }
249 }
250 }
251
252 private static List<HRegionInfo> recreateRegionInfo(final List<HRegionInfo> regions) {
253 ArrayList<HRegionInfo> newRegions = new ArrayList<HRegionInfo>(regions.size());
254 for (HRegionInfo hri: regions) {
255 newRegions.add(new HRegionInfo(hri.getTable(), hri.getStartKey(), hri.getEndKey()));
256 }
257 return newRegions;
258 }
259
260 private boolean prepareTruncate(final MasterProcedureEnv env) throws IOException {
261 try {
262 env.getMasterServices().checkTableModifiable(getTableName());
263 } catch (TableNotFoundException|TableNotDisabledException e) {
264 setFailure("master-truncate-table", e);
265 return false;
266 }
267 return true;
268 }
269
270 private boolean preTruncate(final MasterProcedureEnv env)
271 throws IOException, InterruptedException {
272 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
273 if (cpHost != null) {
274 final TableName tableName = getTableName();
275 user.doAs(new PrivilegedExceptionAction<Void>() {
276 @Override
277 public Void run() throws Exception {
278 cpHost.preTruncateTableHandler(tableName);
279 return null;
280 }
281 });
282 }
283 return true;
284 }
285
286 private void postTruncate(final MasterProcedureEnv env)
287 throws IOException, InterruptedException {
288 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
289 if (cpHost != null) {
290 final TableName tableName = getTableName();
291 user.doAs(new PrivilegedExceptionAction<Void>() {
292 @Override
293 public Void run() throws Exception {
294 cpHost.postTruncateTableHandler(tableName);
295 return null;
296 }
297 });
298 }
299 }
300 }