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.List;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.HRegionInfo;
30 import org.apache.hadoop.hbase.HTableDescriptor;
31 import org.apache.hadoop.hbase.InvalidFamilyOperationException;
32 import org.apache.hadoop.hbase.TableName;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
35 import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
36 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
37 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
38 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteColumnFamilyState;
39 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
40 import org.apache.hadoop.hbase.security.User;
41 import org.apache.hadoop.hbase.util.ByteStringer;
42 import org.apache.hadoop.hbase.util.Bytes;
43
44
45
46
47 @InterfaceAudience.Private
48 public class DeleteColumnFamilyProcedure
49 extends StateMachineProcedure<MasterProcedureEnv, DeleteColumnFamilyState>
50 implements TableProcedureInterface {
51 private static final Log LOG = LogFactory.getLog(DeleteColumnFamilyProcedure.class);
52
53 private final AtomicBoolean aborted = new AtomicBoolean(false);
54
55 private HTableDescriptor unmodifiedHTableDescriptor;
56 private TableName tableName;
57 private byte [] familyName;
58 private User user;
59
60 private List<HRegionInfo> regionInfoList;
61 private Boolean traceEnabled;
62
63 public DeleteColumnFamilyProcedure() {
64 this.unmodifiedHTableDescriptor = null;
65 this.regionInfoList = null;
66 this.traceEnabled = null;
67 }
68
69 public DeleteColumnFamilyProcedure(final MasterProcedureEnv env, final TableName tableName,
70 final byte[] familyName) {
71 this.tableName = tableName;
72 this.familyName = familyName;
73 this.user = env.getRequestUser();
74 this.setOwner(this.user.getShortName());
75 this.unmodifiedHTableDescriptor = null;
76 this.regionInfoList = null;
77 this.traceEnabled = null;
78 }
79
80 @Override
81 protected Flow executeFromState(final MasterProcedureEnv env, DeleteColumnFamilyState state)
82 throws InterruptedException {
83 if (isTraceEnabled()) {
84 LOG.trace(this + " execute state=" + state);
85 }
86
87 try {
88 switch (state) {
89 case DELETE_COLUMN_FAMILY_PREPARE:
90 prepareDelete(env);
91 setNextState(DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_PRE_OPERATION);
92 break;
93 case DELETE_COLUMN_FAMILY_PRE_OPERATION:
94 preDelete(env, state);
95 setNextState(DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR);
96 break;
97 case DELETE_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR:
98 updateTableDescriptor(env);
99 setNextState(DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT);
100 break;
101 case DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT:
102 deleteFromFs(env);
103 setNextState(DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_POST_OPERATION);
104 break;
105 case DELETE_COLUMN_FAMILY_POST_OPERATION:
106 postDelete(env, state);
107 setNextState(DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_REOPEN_ALL_REGIONS);
108 break;
109 case DELETE_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
110 reOpenAllRegionsIfTableIsOnline(env);
111 return Flow.NO_MORE_STATE;
112 default:
113 throw new UnsupportedOperationException(this + " unhandled state=" + state);
114 }
115 } catch (IOException e) {
116 if (!isRollbackSupported(state)) {
117
118 LOG.warn("Error trying to delete the column family " + getColumnFamilyName()
119 + " from table " + tableName + "(in state=" + state + ")", e);
120 } else {
121 LOG.error("Error trying to delete the column family " + getColumnFamilyName()
122 + " from table " + tableName + "(in state=" + state + ")", e);
123 setFailure("master-delete-column-family", e);
124 }
125 }
126 return Flow.HAS_MORE_STATE;
127 }
128
129 @Override
130 protected void rollbackState(final MasterProcedureEnv env, final DeleteColumnFamilyState state)
131 throws IOException {
132 if (isTraceEnabled()) {
133 LOG.trace(this + " rollback state=" + state);
134 }
135 try {
136 switch (state) {
137 case DELETE_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
138 break;
139 case DELETE_COLUMN_FAMILY_POST_OPERATION:
140
141 break;
142 case DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT:
143
144
145
146 throw new UnsupportedOperationException(this + " rollback of state=" + state
147 + " is unsupported.");
148 case DELETE_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR:
149 restoreTableDescriptor(env);
150 break;
151 case DELETE_COLUMN_FAMILY_PRE_OPERATION:
152
153 break;
154 case DELETE_COLUMN_FAMILY_PREPARE:
155 break;
156 default:
157 throw new UnsupportedOperationException(this + " unhandled state=" + state);
158 }
159 } catch (IOException e) {
160
161
162 LOG.warn("Failed rollback attempt step " + state + " for deleting the column family"
163 + getColumnFamilyName() + " to the table " + tableName, e);
164 throw e;
165 }
166 }
167
168 @Override
169 protected DeleteColumnFamilyState getState(final int stateId) {
170 return DeleteColumnFamilyState.valueOf(stateId);
171 }
172
173 @Override
174 protected int getStateId(final DeleteColumnFamilyState state) {
175 return state.getNumber();
176 }
177
178 @Override
179 protected DeleteColumnFamilyState getInitialState() {
180 return DeleteColumnFamilyState.DELETE_COLUMN_FAMILY_PREPARE;
181 }
182
183 @Override
184 protected void setNextState(DeleteColumnFamilyState state) {
185 if (aborted.get() && isRollbackSupported(state)) {
186 setAbortFailure("delete-columnfamily", "abort requested");
187 } else {
188 super.setNextState(state);
189 }
190 }
191
192 @Override
193 public boolean abort(final MasterProcedureEnv env) {
194 aborted.set(true);
195 return true;
196 }
197
198 @Override
199 protected boolean acquireLock(final MasterProcedureEnv env) {
200 if (env.waitInitialized(this)) return false;
201 return env.getProcedureQueue().tryAcquireTableExclusiveLock(this, tableName);
202 }
203
204 @Override
205 protected void releaseLock(final MasterProcedureEnv env) {
206 env.getProcedureQueue().releaseTableExclusiveLock(this, tableName);
207 }
208
209 @Override
210 public void serializeStateData(final OutputStream stream) throws IOException {
211 super.serializeStateData(stream);
212
213 MasterProcedureProtos.DeleteColumnFamilyStateData.Builder deleteCFMsg =
214 MasterProcedureProtos.DeleteColumnFamilyStateData.newBuilder()
215 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(user))
216 .setTableName(ProtobufUtil.toProtoTableName(tableName))
217 .setColumnfamilyName(ByteStringer.wrap(familyName));
218 if (unmodifiedHTableDescriptor != null) {
219 deleteCFMsg.setUnmodifiedTableSchema(unmodifiedHTableDescriptor.convert());
220 }
221
222 deleteCFMsg.build().writeDelimitedTo(stream);
223 }
224
225 @Override
226 public void deserializeStateData(final InputStream stream) throws IOException {
227 super.deserializeStateData(stream);
228 MasterProcedureProtos.DeleteColumnFamilyStateData deleteCFMsg =
229 MasterProcedureProtos.DeleteColumnFamilyStateData.parseDelimitedFrom(stream);
230 user = MasterProcedureUtil.toUserInfo(deleteCFMsg.getUserInfo());
231 tableName = ProtobufUtil.toTableName(deleteCFMsg.getTableName());
232 familyName = deleteCFMsg.getColumnfamilyName().toByteArray();
233
234 if (deleteCFMsg.hasUnmodifiedTableSchema()) {
235 unmodifiedHTableDescriptor = HTableDescriptor.convert(deleteCFMsg.getUnmodifiedTableSchema());
236 }
237 }
238
239 @Override
240 public void toStringClassDetails(StringBuilder sb) {
241 sb.append(getClass().getSimpleName());
242 sb.append(" (table=");
243 sb.append(tableName);
244 sb.append(", columnfamily=");
245 if (familyName != null) {
246 sb.append(getColumnFamilyName());
247 } else {
248 sb.append("Unknown");
249 }
250 sb.append(")");
251 }
252
253 @Override
254 public TableName getTableName() {
255 return tableName;
256 }
257
258 @Override
259 public TableOperationType getTableOperationType() {
260 return TableOperationType.EDIT;
261 }
262
263
264
265
266
267
268 private void prepareDelete(final MasterProcedureEnv env) throws IOException {
269
270 MasterDDLOperationHelper.checkTableModifiable(env, tableName);
271
272
273 unmodifiedHTableDescriptor = env.getMasterServices().getTableDescriptors().get(tableName);
274 if (unmodifiedHTableDescriptor == null) {
275 throw new IOException("HTableDescriptor missing for " + tableName);
276 }
277 if (!unmodifiedHTableDescriptor.hasFamily(familyName)) {
278 throw new InvalidFamilyOperationException("Family '" + getColumnFamilyName()
279 + "' does not exist, so it cannot be deleted");
280 }
281
282 if (unmodifiedHTableDescriptor.getColumnFamilies().length == 1) {
283 throw new InvalidFamilyOperationException("Family '" + getColumnFamilyName()
284 + "' is the only column family in the table, so it cannot be deleted");
285 }
286 }
287
288
289
290
291
292
293
294
295 private void preDelete(final MasterProcedureEnv env, final DeleteColumnFamilyState state)
296 throws IOException, InterruptedException {
297 runCoprocessorAction(env, state);
298 }
299
300
301
302
303 private void updateTableDescriptor(final MasterProcedureEnv env) throws IOException {
304
305 LOG.info("DeleteColumn. Table = " + tableName + " family = " + getColumnFamilyName());
306
307 HTableDescriptor htd = env.getMasterServices().getTableDescriptors().get(tableName);
308
309 if (!htd.hasFamily(familyName)) {
310
311
312
313 return;
314 }
315
316 htd.removeFamily(familyName);
317 env.getMasterServices().getTableDescriptors().add(htd);
318 }
319
320
321
322
323
324
325 private void restoreTableDescriptor(final MasterProcedureEnv env) throws IOException {
326 env.getMasterServices().getTableDescriptors().add(unmodifiedHTableDescriptor);
327
328
329 reOpenAllRegionsIfTableIsOnline(env);
330 }
331
332
333
334
335 private void deleteFromFs(final MasterProcedureEnv env) throws IOException {
336 MasterDDLOperationHelper.deleteColumnFamilyFromFileSystem(env, tableName,
337 getRegionInfoList(env), familyName);
338 }
339
340
341
342
343
344
345
346
347 private void postDelete(final MasterProcedureEnv env, final DeleteColumnFamilyState state)
348 throws IOException, InterruptedException {
349 runCoprocessorAction(env, state);
350 }
351
352
353
354
355
356
357 private void reOpenAllRegionsIfTableIsOnline(final MasterProcedureEnv env) throws IOException {
358
359 if (!env.getMasterServices().getAssignmentManager().getTableStateManager()
360 .isTableState(getTableName(), ZooKeeperProtos.Table.State.ENABLED)) {
361 return;
362 }
363
364 if (MasterDDLOperationHelper.reOpenAllRegions(env, getTableName(), getRegionInfoList(env))) {
365 LOG.info("Completed delete column family operation on table " + getTableName());
366 } else {
367 LOG.warn("Error on reopening the regions on table " + getTableName());
368 }
369 }
370
371
372
373
374
375
376 private Boolean isTraceEnabled() {
377 if (traceEnabled == null) {
378 traceEnabled = LOG.isTraceEnabled();
379 }
380 return traceEnabled;
381 }
382
383 private String getColumnFamilyName() {
384 return Bytes.toString(familyName);
385 }
386
387
388
389
390
391
392
393
394 private void runCoprocessorAction(final MasterProcedureEnv env,
395 final DeleteColumnFamilyState state) throws IOException, InterruptedException {
396 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
397 if (cpHost != null) {
398 switch (state) {
399 case DELETE_COLUMN_FAMILY_PRE_OPERATION:
400 cpHost.preDeleteColumnHandler(tableName, familyName, user);
401 break;
402 case DELETE_COLUMN_FAMILY_POST_OPERATION:
403 cpHost.postDeleteColumnHandler(tableName, familyName, user);
404 break;
405 default:
406 throw new UnsupportedOperationException(this + " unhandled state=" + state);
407 }
408 }
409 }
410
411
412
413
414 private boolean isRollbackSupported(final DeleteColumnFamilyState state) {
415 switch (state) {
416 case DELETE_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
417 case DELETE_COLUMN_FAMILY_POST_OPERATION:
418 case DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT:
419
420 return false;
421 default:
422 break;
423 }
424 return true;
425 }
426
427 private List<HRegionInfo> getRegionInfoList(final MasterProcedureEnv env) throws IOException {
428 if (regionInfoList == null) {
429 regionInfoList = ProcedureSyncWait.getRegionsFromMeta(env, getTableName());
430 }
431 return regionInfoList;
432 }
433 }