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.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.hbase.HRegionInfo;
31 import org.apache.hadoop.hbase.HTableDescriptor;
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.classification.InterfaceAudience;
36 import org.apache.hadoop.hbase.exceptions.HBaseException;
37 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
38 import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
39 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
40 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
41 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
42 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.TruncateTableState;
43 import org.apache.hadoop.hbase.security.User;
44 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
45
46 @InterfaceAudience.Private
47 public class TruncateTableProcedure
48 extends StateMachineProcedure<MasterProcedureEnv, TruncateTableState>
49 implements TableProcedureInterface {
50 private static final Log LOG = LogFactory.getLog(TruncateTableProcedure.class);
51
52 private boolean preserveSplits;
53 private List<HRegionInfo> regions;
54 private User user;
55 private HTableDescriptor hTableDescriptor;
56 private TableName tableName;
57
58 public TruncateTableProcedure() {
59
60 }
61
62 public TruncateTableProcedure(final MasterProcedureEnv env, final TableName tableName,
63 boolean preserveSplits) {
64 this.tableName = tableName;
65 this.preserveSplits = preserveSplits;
66 this.user = env.getRequestUser();
67 this.setOwner(this.user.getShortName());
68 }
69
70 @Override
71 protected Flow executeFromState(final MasterProcedureEnv env, TruncateTableState state)
72 throws InterruptedException {
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(), true, true);
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 setNextState(TruncateTableState.TRUNCATE_TABLE_CREATE_FS_LAYOUT);
105 if (!preserveSplits) {
106
107 regions = Arrays.asList(ModifyRegionUtils.createHRegionInfos(hTableDescriptor, null));
108 } else {
109 regions = recreateRegionInfo(regions);
110 }
111 break;
112 case TRUNCATE_TABLE_CREATE_FS_LAYOUT:
113 DeleteTableProcedure.deleteFromFs(env, getTableName(), regions, true);
114 regions = CreateTableProcedure.createFsLayout(env, hTableDescriptor, regions);
115 CreateTableProcedure.updateTableDescCache(env, getTableName());
116 setNextState(TruncateTableState.TRUNCATE_TABLE_ADD_TO_META);
117 break;
118 case TRUNCATE_TABLE_ADD_TO_META:
119 regions = CreateTableProcedure.addTableToMeta(env, hTableDescriptor, regions);
120 setNextState(TruncateTableState.TRUNCATE_TABLE_ASSIGN_REGIONS);
121 break;
122 case TRUNCATE_TABLE_ASSIGN_REGIONS:
123 CreateTableProcedure.assignRegions(env, getTableName(), regions);
124 setNextState(TruncateTableState.TRUNCATE_TABLE_POST_OPERATION);
125 hTableDescriptor = null;
126 regions = null;
127 break;
128 case TRUNCATE_TABLE_POST_OPERATION:
129 postTruncate(env);
130 LOG.debug("truncate '" + getTableName() + "' completed");
131 return Flow.NO_MORE_STATE;
132 default:
133 throw new UnsupportedOperationException("unhandled state=" + state);
134 }
135 } catch (HBaseException|IOException e) {
136 LOG.warn("Retriable error 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.waitInitialized(this)) {
187 return false;
188 }
189 return env.getProcedureQueue().tryAcquireTableExclusiveLock(this, getTableName());
190 }
191
192 @Override
193 protected void releaseLock(final MasterProcedureEnv env) {
194 env.getProcedureQueue().releaseTableExclusiveLock(this, 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 cpHost.preTruncateTableHandler(tableName, user);
276 }
277 return true;
278 }
279
280 private void postTruncate(final MasterProcedureEnv env)
281 throws IOException, InterruptedException {
282 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
283 if (cpHost != null) {
284 final TableName tableName = getTableName();
285 cpHost.postTruncateTableHandler(tableName, user);
286 }
287 }
288
289 HRegionInfo getFirstRegionInfo() {
290 if (regions == null || regions.isEmpty()) {
291 return null;
292 }
293 return regions.get(0);
294 }
295 }