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