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.HColumnDescriptor;
31 import org.apache.hadoop.hbase.HRegionInfo;
32 import org.apache.hadoop.hbase.HTableDescriptor;
33 import org.apache.hadoop.hbase.InvalidFamilyOperationException;
34 import org.apache.hadoop.hbase.TableName;
35 import org.apache.hadoop.hbase.classification.InterfaceAudience;
36 import org.apache.hadoop.hbase.executor.EventType;
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.MasterProcedureProtos;
41 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.ModifyColumnFamilyState;
42 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
43 import org.apache.hadoop.security.UserGroupInformation;
44
45
46
47
48 @InterfaceAudience.Private
49 public class ModifyColumnFamilyProcedure
50 extends StateMachineProcedure<MasterProcedureEnv, ModifyColumnFamilyState>
51 implements TableProcedureInterface {
52 private static final Log LOG = LogFactory.getLog(ModifyColumnFamilyProcedure.class);
53
54 private final AtomicBoolean aborted = new AtomicBoolean(false);
55
56 private TableName tableName;
57 private HTableDescriptor unmodifiedHTableDescriptor;
58 private HColumnDescriptor cfDescriptor;
59 private UserGroupInformation user;
60
61 private Boolean traceEnabled;
62
63 public ModifyColumnFamilyProcedure() {
64 this.unmodifiedHTableDescriptor = null;
65 this.traceEnabled = null;
66 }
67
68 public ModifyColumnFamilyProcedure(final MasterProcedureEnv env, final TableName tableName,
69 final HColumnDescriptor cfDescriptor) {
70 this.tableName = tableName;
71 this.cfDescriptor = cfDescriptor;
72 this.user = env.getRequestUser().getUGI();
73 this.setOwner(this.user.getShortUserName());
74 this.unmodifiedHTableDescriptor = null;
75 this.traceEnabled = null;
76 }
77
78 @Override
79 protected Flow executeFromState(final MasterProcedureEnv env,
80 final ModifyColumnFamilyState state) {
81 if (isTraceEnabled()) {
82 LOG.trace(this + " execute state=" + state);
83 }
84
85 try {
86 switch (state) {
87 case MODIFY_COLUMN_FAMILY_PREPARE:
88 prepareModify(env);
89 setNextState(ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_PRE_OPERATION);
90 break;
91 case MODIFY_COLUMN_FAMILY_PRE_OPERATION:
92 preModify(env, state);
93 setNextState(ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR);
94 break;
95 case MODIFY_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR:
96 updateTableDescriptor(env);
97 setNextState(ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_POST_OPERATION);
98 break;
99 case MODIFY_COLUMN_FAMILY_POST_OPERATION:
100 postModify(env, state);
101 setNextState(ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_REOPEN_ALL_REGIONS);
102 break;
103 case MODIFY_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
104 reOpenAllRegionsIfTableIsOnline(env);
105 return Flow.NO_MORE_STATE;
106 default:
107 throw new UnsupportedOperationException(this + " unhandled state=" + state);
108 }
109 } catch (InterruptedException|IOException e) {
110 LOG.warn("Error trying to modify the column family " + getColumnFamilyName()
111 + " of the table " + tableName + "(in state=" + state + ")", e);
112
113 setFailure("master-modify-columnfamily", e);
114 }
115 return Flow.HAS_MORE_STATE;
116 }
117
118 @Override
119 protected void rollbackState(final MasterProcedureEnv env, final ModifyColumnFamilyState state)
120 throws IOException {
121 if (isTraceEnabled()) {
122 LOG.trace(this + " rollback state=" + state);
123 }
124 try {
125 switch (state) {
126 case MODIFY_COLUMN_FAMILY_REOPEN_ALL_REGIONS:
127 break;
128 case MODIFY_COLUMN_FAMILY_POST_OPERATION:
129
130 break;
131 case MODIFY_COLUMN_FAMILY_UPDATE_TABLE_DESCRIPTOR:
132 restoreTableDescriptor(env);
133 break;
134 case MODIFY_COLUMN_FAMILY_PRE_OPERATION:
135
136 break;
137 case MODIFY_COLUMN_FAMILY_PREPARE:
138 break;
139 default:
140 throw new UnsupportedOperationException(this + " unhandled state=" + state);
141 }
142 } catch (IOException e) {
143
144
145 LOG.warn("Failed rollback attempt step " + state + " for adding the column family"
146 + getColumnFamilyName() + " to the table " + tableName, e);
147 throw e;
148 }
149 }
150
151 @Override
152 protected ModifyColumnFamilyState getState(final int stateId) {
153 return ModifyColumnFamilyState.valueOf(stateId);
154 }
155
156 @Override
157 protected int getStateId(final ModifyColumnFamilyState state) {
158 return state.getNumber();
159 }
160
161 @Override
162 protected ModifyColumnFamilyState getInitialState() {
163 return ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_PREPARE;
164 }
165
166 @Override
167 protected void setNextState(ModifyColumnFamilyState state) {
168 if (aborted.get()) {
169 setAbortFailure("modify-columnfamily", "abort requested");
170 } else {
171 super.setNextState(state);
172 }
173 }
174
175 @Override
176 public boolean abort(final MasterProcedureEnv env) {
177 aborted.set(true);
178 return true;
179 }
180
181 @Override
182 protected boolean acquireLock(final MasterProcedureEnv env) {
183 if (!env.isInitialized()) return false;
184 return env.getProcedureQueue().tryAcquireTableWrite(
185 tableName,
186 EventType.C_M_MODIFY_FAMILY.toString());
187 }
188
189 @Override
190 protected void releaseLock(final MasterProcedureEnv env) {
191 env.getProcedureQueue().releaseTableWrite(tableName);
192 }
193
194 @Override
195 public void serializeStateData(final OutputStream stream) throws IOException {
196 super.serializeStateData(stream);
197
198 MasterProcedureProtos.ModifyColumnFamilyStateData.Builder modifyCFMsg =
199 MasterProcedureProtos.ModifyColumnFamilyStateData.newBuilder()
200 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(user))
201 .setTableName(ProtobufUtil.toProtoTableName(tableName))
202 .setColumnfamilySchema(cfDescriptor.convert());
203 if (unmodifiedHTableDescriptor != null) {
204 modifyCFMsg.setUnmodifiedTableSchema(unmodifiedHTableDescriptor.convert());
205 }
206
207 modifyCFMsg.build().writeDelimitedTo(stream);
208 }
209
210 @Override
211 public void deserializeStateData(final InputStream stream) throws IOException {
212 super.deserializeStateData(stream);
213
214 MasterProcedureProtos.ModifyColumnFamilyStateData modifyCFMsg =
215 MasterProcedureProtos.ModifyColumnFamilyStateData.parseDelimitedFrom(stream);
216 user = MasterProcedureUtil.toUserInfo(modifyCFMsg.getUserInfo());
217 tableName = ProtobufUtil.toTableName(modifyCFMsg.getTableName());
218 cfDescriptor = HColumnDescriptor.convert(modifyCFMsg.getColumnfamilySchema());
219 if (modifyCFMsg.hasUnmodifiedTableSchema()) {
220 unmodifiedHTableDescriptor = HTableDescriptor.convert(modifyCFMsg.getUnmodifiedTableSchema());
221 }
222 }
223
224 @Override
225 public void toStringClassDetails(StringBuilder sb) {
226 sb.append(getClass().getSimpleName());
227 sb.append(" (table=");
228 sb.append(tableName);
229 sb.append(", columnfamily=");
230 if (cfDescriptor != null) {
231 sb.append(getColumnFamilyName());
232 } else {
233 sb.append("Unknown");
234 }
235 sb.append(")");
236 }
237
238 @Override
239 public TableName getTableName() {
240 return tableName;
241 }
242
243 @Override
244 public TableOperationType getTableOperationType() {
245 return TableOperationType.EDIT;
246 }
247
248
249
250
251
252
253 private void prepareModify(final MasterProcedureEnv env) throws IOException {
254
255 MasterDDLOperationHelper.checkTableModifiable(env, tableName);
256
257 unmodifiedHTableDescriptor = env.getMasterServices().getTableDescriptors().get(tableName);
258 if (unmodifiedHTableDescriptor == null) {
259 throw new IOException("HTableDescriptor missing for " + tableName);
260 }
261 if (!unmodifiedHTableDescriptor.hasFamily(cfDescriptor.getName())) {
262 throw new InvalidFamilyOperationException("Family '" + getColumnFamilyName()
263 + "' does not exist, so it cannot be modified");
264 }
265 }
266
267
268
269
270
271
272
273
274 private void preModify(final MasterProcedureEnv env, final ModifyColumnFamilyState state)
275 throws IOException, InterruptedException {
276 runCoprocessorAction(env, state);
277 }
278
279
280
281
282 private void updateTableDescriptor(final MasterProcedureEnv env) throws IOException {
283
284 LOG.info("ModifyColumnFamily. Table = " + tableName + " HCD = " + cfDescriptor.toString());
285
286 HTableDescriptor htd = env.getMasterServices().getTableDescriptors().get(tableName);
287 htd.modifyFamily(cfDescriptor);
288 env.getMasterServices().getTableDescriptors().add(htd);
289 }
290
291
292
293
294
295
296 private void restoreTableDescriptor(final MasterProcedureEnv env) throws IOException {
297 env.getMasterServices().getTableDescriptors().add(unmodifiedHTableDescriptor);
298
299
300 reOpenAllRegionsIfTableIsOnline(env);
301 }
302
303
304
305
306
307
308
309
310 private void postModify(final MasterProcedureEnv env, final ModifyColumnFamilyState state)
311 throws IOException, InterruptedException {
312 runCoprocessorAction(env, state);
313 }
314
315
316
317
318
319
320 private void reOpenAllRegionsIfTableIsOnline(final MasterProcedureEnv env) throws IOException {
321
322 if (!env.getMasterServices().getAssignmentManager().getTableStateManager()
323 .isTableState(getTableName(), ZooKeeperProtos.Table.State.ENABLED)) {
324 return;
325 }
326
327 List<HRegionInfo> regionInfoList = ProcedureSyncWait.getRegionsFromMeta(env, getTableName());
328 if (MasterDDLOperationHelper.reOpenAllRegions(env, getTableName(), regionInfoList)) {
329 LOG.info("Completed add column family operation on table " + getTableName());
330 } else {
331 LOG.warn("Error on reopening the regions on table " + getTableName());
332 }
333 }
334
335
336
337
338
339
340 private Boolean isTraceEnabled() {
341 if (traceEnabled == null) {
342 traceEnabled = LOG.isTraceEnabled();
343 }
344 return traceEnabled;
345 }
346
347 private String getColumnFamilyName() {
348 return cfDescriptor.getNameAsString();
349 }
350
351
352
353
354
355
356
357
358 private void runCoprocessorAction(final MasterProcedureEnv env,
359 final ModifyColumnFamilyState state) throws IOException, InterruptedException {
360 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
361 if (cpHost != null) {
362 user.doAs(new PrivilegedExceptionAction<Void>() {
363 @Override
364 public Void run() throws Exception {
365 switch (state) {
366 case MODIFY_COLUMN_FAMILY_PRE_OPERATION:
367 cpHost.preModifyColumnHandler(tableName, cfDescriptor);
368 break;
369 case MODIFY_COLUMN_FAMILY_POST_OPERATION:
370 cpHost.postModifyColumnHandler(tableName, cfDescriptor);
371 break;
372 default:
373 throw new UnsupportedOperationException(this + " unhandled state=" + state);
374 }
375 return null;
376 }
377 });
378 }
379 }
380 }