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 throws InterruptedException {
74 if (LOG.isTraceEnabled()) {
75 LOG.trace(this + " execute state=" + state);
76 }
77 try {
78 switch (state) {
79 case TRUNCATE_TABLE_PRE_OPERATION:
80
81 if (!prepareTruncate(env)) {
82 assert isFailed() : "the truncate should have an exception here";
83 return Flow.NO_MORE_STATE;
84 }
85
86
87 LOG.debug("waiting for '" + getTableName() + "' regions in transition");
88 regions = ProcedureSyncWait.getRegionsFromMeta(env, getTableName());
89 assert regions != null && !regions.isEmpty() : "unexpected 0 regions";
90 ProcedureSyncWait.waitRegionInTransition(env, regions);
91
92
93 preTruncate(env);
94
95 setNextState(TruncateTableState.TRUNCATE_TABLE_REMOVE_FROM_META);
96 break;
97 case TRUNCATE_TABLE_REMOVE_FROM_META:
98 hTableDescriptor = env.getMasterServices().getTableDescriptors().get(tableName);
99 DeleteTableProcedure.deleteFromMeta(env, getTableName(), regions);
100 DeleteTableProcedure.deleteAssignmentState(env, getTableName());
101 setNextState(TruncateTableState.TRUNCATE_TABLE_CLEAR_FS_LAYOUT);
102 break;
103 case TRUNCATE_TABLE_CLEAR_FS_LAYOUT:
104 DeleteTableProcedure.deleteFromFs(env, getTableName(), regions, true);
105 if (!preserveSplits) {
106
107 regions = Arrays.asList(ModifyRegionUtils.createHRegionInfos(hTableDescriptor, null));
108 }
109 setNextState(TruncateTableState.TRUNCATE_TABLE_CREATE_FS_LAYOUT);
110 break;
111 case TRUNCATE_TABLE_CREATE_FS_LAYOUT:
112 regions = CreateTableProcedure.createFsLayout(env, hTableDescriptor, regions);
113 CreateTableProcedure.updateTableDescCache(env, getTableName());
114 setNextState(TruncateTableState.TRUNCATE_TABLE_ADD_TO_META);
115 break;
116 case TRUNCATE_TABLE_ADD_TO_META:
117 regions = CreateTableProcedure.addTableToMeta(env, hTableDescriptor, regions);
118 setNextState(TruncateTableState.TRUNCATE_TABLE_ASSIGN_REGIONS);
119 break;
120 case TRUNCATE_TABLE_ASSIGN_REGIONS:
121 CreateTableProcedure.assignRegions(env, getTableName(), regions);
122 setNextState(TruncateTableState.TRUNCATE_TABLE_POST_OPERATION);
123 hTableDescriptor = null;
124 regions = null;
125 break;
126 case TRUNCATE_TABLE_POST_OPERATION:
127 postTruncate(env);
128 LOG.debug("truncate '" + getTableName() + "' completed");
129 return Flow.NO_MORE_STATE;
130 default:
131 throw new UnsupportedOperationException("unhandled state=" + state);
132 }
133 } catch (HBaseException|IOException e) {
134 LOG.warn("Retriable error trying to truncate table=" + getTableName() + " state=" + state, e);
135 }
136 return Flow.HAS_MORE_STATE;
137 }
138
139 @Override
140 protected void rollbackState(final MasterProcedureEnv env, final TruncateTableState state) {
141 if (state == TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION) {
142
143
144 return;
145 }
146
147
148 throw new UnsupportedOperationException("unhandled state=" + state);
149 }
150
151 @Override
152 protected TruncateTableState getState(final int stateId) {
153 return TruncateTableState.valueOf(stateId);
154 }
155
156 @Override
157 protected int getStateId(final TruncateTableState state) {
158 return state.getNumber();
159 }
160
161 @Override
162 protected TruncateTableState getInitialState() {
163 return TruncateTableState.TRUNCATE_TABLE_PRE_OPERATION;
164 }
165
166 @Override
167 public TableName getTableName() {
168 return tableName;
169 }
170
171 @Override
172 public TableOperationType getTableOperationType() {
173 return TableOperationType.EDIT;
174 }
175
176 @Override
177 public boolean abort(final MasterProcedureEnv env) {
178
179 return false;
180 }
181
182 @Override
183 protected boolean acquireLock(final MasterProcedureEnv env) {
184 if (env.waitInitialized(this)) return false;
185 return env.getProcedureQueue().tryAcquireTableExclusiveLock(this, getTableName());
186 }
187
188 @Override
189 protected void releaseLock(final MasterProcedureEnv env) {
190 env.getProcedureQueue().releaseTableExclusiveLock(this, getTableName());
191 }
192
193 @Override
194 public void toStringClassDetails(StringBuilder sb) {
195 sb.append(getClass().getSimpleName());
196 sb.append(" (table=");
197 sb.append(getTableName());
198 sb.append(" preserveSplits=");
199 sb.append(preserveSplits);
200 sb.append(")");
201 }
202
203 @Override
204 public void serializeStateData(final OutputStream stream) throws IOException {
205 super.serializeStateData(stream);
206
207 MasterProcedureProtos.TruncateTableStateData.Builder state =
208 MasterProcedureProtos.TruncateTableStateData.newBuilder()
209 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(this.user))
210 .setPreserveSplits(preserveSplits);
211 if (hTableDescriptor != null) {
212 state.setTableSchema(hTableDescriptor.convert());
213 } else {
214 state.setTableName(ProtobufUtil.toProtoTableName(tableName));
215 }
216 if (regions != null) {
217 for (HRegionInfo hri: regions) {
218 state.addRegionInfo(HRegionInfo.convert(hri));
219 }
220 }
221 state.build().writeDelimitedTo(stream);
222 }
223
224 @Override
225 public void deserializeStateData(final InputStream stream) throws IOException {
226 super.deserializeStateData(stream);
227
228 MasterProcedureProtos.TruncateTableStateData state =
229 MasterProcedureProtos.TruncateTableStateData.parseDelimitedFrom(stream);
230 user = MasterProcedureUtil.toUserInfo(state.getUserInfo());
231 if (state.hasTableSchema()) {
232 hTableDescriptor = HTableDescriptor.convert(state.getTableSchema());
233 tableName = hTableDescriptor.getTableName();
234 } else {
235 tableName = ProtobufUtil.toTableName(state.getTableName());
236 }
237 preserveSplits = state.getPreserveSplits();
238 if (state.getRegionInfoCount() == 0) {
239 regions = null;
240 } else {
241 regions = new ArrayList<HRegionInfo>(state.getRegionInfoCount());
242 for (HBaseProtos.RegionInfo hri: state.getRegionInfoList()) {
243 regions.add(HRegionInfo.convert(hri));
244 }
245 }
246 }
247
248 private boolean prepareTruncate(final MasterProcedureEnv env) throws IOException {
249 try {
250 env.getMasterServices().checkTableModifiable(getTableName());
251 } catch (TableNotFoundException|TableNotDisabledException e) {
252 setFailure("master-truncate-table", e);
253 return false;
254 }
255 return true;
256 }
257
258 private boolean preTruncate(final MasterProcedureEnv env)
259 throws IOException, InterruptedException {
260 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
261 if (cpHost != null) {
262 final TableName tableName = getTableName();
263 user.doAs(new PrivilegedExceptionAction<Void>() {
264 @Override
265 public Void run() throws Exception {
266 cpHost.preTruncateTableHandler(tableName);
267 return null;
268 }
269 });
270 }
271 return true;
272 }
273
274 private void postTruncate(final MasterProcedureEnv env)
275 throws IOException, InterruptedException {
276 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
277 if (cpHost != null) {
278 final TableName tableName = getTableName();
279 user.doAs(new PrivilegedExceptionAction<Void>() {
280 @Override
281 public Void run() throws Exception {
282 cpHost.postTruncateTableHandler(tableName);
283 return null;
284 }
285 });
286 }
287 }
288 }