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.master.MasterCoprocessorHost;
36 import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
37 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
38 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
39 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteColumnFamilyState;
40 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
41 import org.apache.hadoop.hbase.util.ByteStringer;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.security.UserGroupInformation;
44
45
46
47
48 @InterfaceAudience.Private
49 public class DeleteColumnFamilyProcedure
50 extends StateMachineProcedure<MasterProcedureEnv, DeleteColumnFamilyState>
51 implements TableProcedureInterface {
52 private static final Log LOG = LogFactory.getLog(DeleteColumnFamilyProcedure.class);
53
54 private final AtomicBoolean aborted = new AtomicBoolean(false);
55
56 private HTableDescriptor unmodifiedHTableDescriptor;
57 private TableName tableName;
58 private byte [] familyName;
59 private boolean hasMob;
60 private UserGroupInformation user;
61
62 private List<HRegionInfo> regionInfoList;
63 private Boolean traceEnabled;
64
65 public DeleteColumnFamilyProcedure() {
66 this.unmodifiedHTableDescriptor = null;
67 this.regionInfoList = null;
68 this.traceEnabled = null;
69 }
70
71 public DeleteColumnFamilyProcedure(
72 final MasterProcedureEnv env,
73 final TableName tableName,
74 final byte[] familyName) throws IOException {
75 this.tableName = tableName;
76 this.familyName = familyName;
77 this.user = env.getRequestUser().getUGI();
78 this.setOwner(this.user.getShortUserName());
79 this.unmodifiedHTableDescriptor = null;
80 this.regionInfoList = null;
81 this.traceEnabled = null;
82 }
83
84 @Override
85 protected Flow executeFromState(final MasterProcedureEnv env, DeleteColumnFamilyState state)
86 throws InterruptedException {
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 (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.waitInitialized(this)) return false;
205 return env.getProcedureQueue().tryAcquireTableExclusiveLock(this, tableName);
206 }
207
208 @Override
209 protected void releaseLock(final MasterProcedureEnv env) {
210 env.getProcedureQueue().releaseTableExclusiveLock(this, tableName);
211 }
212
213 @Override
214 public void serializeStateData(final OutputStream stream) throws IOException {
215 super.serializeStateData(stream);
216
217 MasterProcedureProtos.DeleteColumnFamilyStateData.Builder deleteCFMsg =
218 MasterProcedureProtos.DeleteColumnFamilyStateData.newBuilder()
219 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(user))
220 .setTableName(ProtobufUtil.toProtoTableName(tableName))
221 .setColumnfamilyName(ByteStringer.wrap(familyName));
222 if (unmodifiedHTableDescriptor != null) {
223 deleteCFMsg.setUnmodifiedTableSchema(unmodifiedHTableDescriptor.convert());
224 }
225
226 deleteCFMsg.build().writeDelimitedTo(stream);
227 }
228
229 @Override
230 public void deserializeStateData(final InputStream stream) throws IOException {
231 super.deserializeStateData(stream);
232 MasterProcedureProtos.DeleteColumnFamilyStateData deleteCFMsg =
233 MasterProcedureProtos.DeleteColumnFamilyStateData.parseDelimitedFrom(stream);
234 user = MasterProcedureUtil.toUserInfo(deleteCFMsg.getUserInfo());
235 tableName = ProtobufUtil.toTableName(deleteCFMsg.getTableName());
236 familyName = deleteCFMsg.getColumnfamilyName().toByteArray();
237
238 if (deleteCFMsg.hasUnmodifiedTableSchema()) {
239 unmodifiedHTableDescriptor = HTableDescriptor.convert(deleteCFMsg.getUnmodifiedTableSchema());
240 }
241 }
242
243 @Override
244 public void toStringClassDetails(StringBuilder sb) {
245 sb.append(getClass().getSimpleName());
246 sb.append(" (table=");
247 sb.append(tableName);
248 sb.append(", columnfamily=");
249 if (familyName != null) {
250 sb.append(getColumnFamilyName());
251 } else {
252 sb.append("Unknown");
253 }
254 sb.append(")");
255 }
256
257 @Override
258 public TableName getTableName() {
259 return tableName;
260 }
261
262 @Override
263 public TableOperationType getTableOperationType() {
264 return TableOperationType.EDIT;
265 }
266
267
268
269
270
271
272 private void prepareDelete(final MasterProcedureEnv env) throws IOException {
273
274 MasterDDLOperationHelper.checkTableModifiable(env, tableName);
275
276
277 unmodifiedHTableDescriptor = env.getMasterServices().getTableDescriptors().get(tableName);
278 if (unmodifiedHTableDescriptor == null) {
279 throw new IOException("HTableDescriptor missing for " + tableName);
280 }
281 if (!unmodifiedHTableDescriptor.hasFamily(familyName)) {
282 throw new InvalidFamilyOperationException("Family '" + getColumnFamilyName()
283 + "' does not exist, so it cannot be deleted");
284 }
285
286 if (unmodifiedHTableDescriptor.getColumnFamilies().length == 1) {
287 throw new InvalidFamilyOperationException("Family '" + getColumnFamilyName()
288 + "' is the only column family in the table, so it cannot be deleted");
289 }
290
291 hasMob = unmodifiedHTableDescriptor.getFamily(familyName).isMobEnabled();
292 }
293
294
295
296
297
298
299
300
301 private void preDelete(final MasterProcedureEnv env, final DeleteColumnFamilyState state)
302 throws IOException, InterruptedException {
303 runCoprocessorAction(env, state);
304 }
305
306
307
308
309 private void updateTableDescriptor(final MasterProcedureEnv env) throws IOException {
310
311 LOG.info("DeleteColumn. Table = " + tableName + " family = " + getColumnFamilyName());
312
313 HTableDescriptor htd = env.getMasterServices().getTableDescriptors().get(tableName);
314
315 if (!htd.hasFamily(familyName)) {
316
317
318
319 return;
320 }
321
322 htd.removeFamily(familyName);
323 env.getMasterServices().getTableDescriptors().add(htd);
324 }
325
326
327
328
329
330
331 private void restoreTableDescriptor(final MasterProcedureEnv env) throws IOException {
332 env.getMasterServices().getTableDescriptors().add(unmodifiedHTableDescriptor);
333
334
335 reOpenAllRegionsIfTableIsOnline(env);
336 }
337
338
339
340
341 private void deleteFromFs(final MasterProcedureEnv env) throws IOException {
342 MasterDDLOperationHelper.deleteColumnFamilyFromFileSystem(env, tableName,
343 getRegionInfoList(env), familyName, hasMob);
344 }
345
346
347
348
349
350
351
352
353 private void postDelete(final MasterProcedureEnv env, final DeleteColumnFamilyState state)
354 throws IOException, InterruptedException {
355 runCoprocessorAction(env, state);
356 }
357
358
359
360
361
362
363 private void reOpenAllRegionsIfTableIsOnline(final MasterProcedureEnv env) throws IOException {
364
365 if (!env.getMasterServices().getAssignmentManager().getTableStateManager()
366 .isTableState(getTableName(), ZooKeeperProtos.Table.State.ENABLED)) {
367 return;
368 }
369
370 if (MasterDDLOperationHelper.reOpenAllRegions(env, getTableName(), getRegionInfoList(env))) {
371 LOG.info("Completed delete column family operation on table " + getTableName());
372 } else {
373 LOG.warn("Error on reopening the regions on table " + getTableName());
374 }
375 }
376
377
378
379
380
381
382 private Boolean isTraceEnabled() {
383 if (traceEnabled == null) {
384 traceEnabled = LOG.isTraceEnabled();
385 }
386 return traceEnabled;
387 }
388
389 private String getColumnFamilyName() {
390 return Bytes.toString(familyName);
391 }
392
393
394
395
396
397
398
399
400 private void runCoprocessorAction(final MasterProcedureEnv env,
401 final DeleteColumnFamilyState state) throws IOException, InterruptedException {
402 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
403 if (cpHost != null) {
404 user.doAs(new PrivilegedExceptionAction<Void>() {
405 @Override
406 public Void run() throws Exception {
407 switch (state) {
408 case DELETE_COLUMN_FAMILY_PRE_OPERATION:
409 cpHost.preDeleteColumnHandler(tableName, familyName);
410 break;
411 case DELETE_COLUMN_FAMILY_POST_OPERATION:
412 cpHost.postDeleteColumnHandler(tableName, familyName);
413 break;
414 default:
415 throw new UnsupportedOperationException(this + " unhandled state=" + state);
416 }
417 return null;
418 }
419 });
420 }
421 }
422
423
424
425
426 private boolean isRollbackSupported(final DeleteColumnFamilyState state) {
427 switch (state) {
428 case DELETE_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
429 case DELETE_COLUMN_FAMILY_POST_OPERATION:
430 case DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT:
431
432 return false;
433 default:
434 break;
435 }
436 return true;
437 }
438
439 private List<HRegionInfo> getRegionInfoList(final MasterProcedureEnv env) throws IOException {
440 if (regionInfoList == null) {
441 regionInfoList = ProcedureSyncWait.getRegionsFromMeta(env, getTableName());
442 }
443 return regionInfoList;
444 }
445 }