001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.procedure; 019 020import static org.apache.hadoop.hbase.master.procedure.ReopenTableRegionsProcedure.PROGRESSIVE_BATCH_BACKOFF_MILLIS_DEFAULT; 021import static org.apache.hadoop.hbase.master.procedure.ReopenTableRegionsProcedure.PROGRESSIVE_BATCH_BACKOFF_MILLIS_KEY; 022import static org.apache.hadoop.hbase.master.procedure.ReopenTableRegionsProcedure.PROGRESSIVE_BATCH_SIZE_MAX_DISABLED; 023import static org.apache.hadoop.hbase.master.procedure.ReopenTableRegionsProcedure.PROGRESSIVE_BATCH_SIZE_MAX_KEY; 024 025import java.io.IOException; 026import java.util.Arrays; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Set; 031import java.util.function.Supplier; 032import java.util.stream.Collectors; 033import java.util.stream.IntStream; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.ConcurrentTableModificationException; 036import org.apache.hadoop.hbase.DoNotRetryIOException; 037import org.apache.hadoop.hbase.HBaseIOException; 038import org.apache.hadoop.hbase.HConstants; 039import org.apache.hadoop.hbase.TableName; 040import org.apache.hadoop.hbase.TableNotFoundException; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.RegionReplicaUtil; 043import org.apache.hadoop.hbase.client.TableDescriptor; 044import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 045import org.apache.hadoop.hbase.fs.ErasureCodingUtils; 046import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 047import org.apache.hadoop.hbase.master.zksyncer.MetaLocationSyncer; 048import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 049import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerValidationUtils; 050import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 051import org.apache.hadoop.hbase.util.Bytes; 052import org.apache.yetus.audience.InterfaceAudience; 053import org.slf4j.Logger; 054import org.slf4j.LoggerFactory; 055 056import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 057import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.ModifyTableState; 059 060@InterfaceAudience.Private 061public class ModifyTableProcedure extends AbstractStateMachineTableProcedure<ModifyTableState> { 062 private static final Logger LOG = LoggerFactory.getLogger(ModifyTableProcedure.class); 063 064 private TableDescriptor unmodifiedTableDescriptor = null; 065 private TableDescriptor modifiedTableDescriptor; 066 private boolean deleteColumnFamilyInModify; 067 private boolean shouldCheckDescriptor; 068 private boolean reopenRegions; 069 /** 070 * List of column families that cannot be deleted from the hbase:meta table. They are critical to 071 * cluster operation. This is a bit of an odd place to keep this list but then this is the tooling 072 * that does add/remove. Keeping it local! 073 */ 074 private static final List<byte[]> UNDELETABLE_META_COLUMNFAMILIES = 075 Collections.unmodifiableList(Arrays.asList(HConstants.CATALOG_FAMILY, HConstants.TABLE_FAMILY, 076 HConstants.REPLICATION_BARRIER_FAMILY, HConstants.NAMESPACE_FAMILY)); 077 078 public ModifyTableProcedure() { 079 super(); 080 initialize(null, false); 081 } 082 083 public ModifyTableProcedure(final MasterProcedureEnv env, final TableDescriptor htd) 084 throws HBaseIOException { 085 this(env, htd, null); 086 } 087 088 public ModifyTableProcedure(final MasterProcedureEnv env, final TableDescriptor htd, 089 final ProcedurePrepareLatch latch) throws HBaseIOException { 090 this(env, htd, latch, null, false, true); 091 } 092 093 public ModifyTableProcedure(final MasterProcedureEnv env, 094 final TableDescriptor newTableDescriptor, final ProcedurePrepareLatch latch, 095 final TableDescriptor oldTableDescriptor, final boolean shouldCheckDescriptor, 096 final boolean reopenRegions) throws HBaseIOException { 097 super(env, latch); 098 this.reopenRegions = reopenRegions; 099 initialize(oldTableDescriptor, shouldCheckDescriptor); 100 this.modifiedTableDescriptor = newTableDescriptor; 101 preflightChecks(env, null/* No table checks; if changing peers, table can be online */); 102 } 103 104 @Override 105 protected void preflightChecks(MasterProcedureEnv env, Boolean enabled) throws HBaseIOException { 106 super.preflightChecks(env, enabled); 107 if (this.modifiedTableDescriptor.isMetaTable()) { 108 // If we are modifying the hbase:meta table, make sure we are not deleting critical 109 // column families else we'll damage the cluster. 110 Set<byte[]> cfs = this.modifiedTableDescriptor.getColumnFamilyNames(); 111 for (byte[] family : UNDELETABLE_META_COLUMNFAMILIES) { 112 if (!cfs.contains(family)) { 113 throw new HBaseIOException( 114 "Delete of hbase:meta column family " + Bytes.toString(family)); 115 } 116 } 117 } 118 119 if (!reopenRegions) { 120 if (this.unmodifiedTableDescriptor == null) { 121 throw new HBaseIOException( 122 "unmodifiedTableDescriptor cannot be null when this table modification won't reopen regions"); 123 } 124 if ( 125 !this.unmodifiedTableDescriptor.getTableName() 126 .equals(this.modifiedTableDescriptor.getTableName()) 127 ) { 128 throw new HBaseIOException( 129 "Cannot change the table name when this modification won't " + "reopen regions."); 130 } 131 if ( 132 this.unmodifiedTableDescriptor.getColumnFamilyCount() 133 != this.modifiedTableDescriptor.getColumnFamilyCount() 134 ) { 135 throw new HBaseIOException( 136 "Cannot add or remove column families when this modification " + "won't reopen regions."); 137 } 138 if ( 139 this.unmodifiedTableDescriptor.getCoprocessorDescriptors().hashCode() 140 != this.modifiedTableDescriptor.getCoprocessorDescriptors().hashCode() 141 ) { 142 throw new HBaseIOException( 143 "Can not modify Coprocessor when table modification won't reopen regions"); 144 } 145 final Set<String> s = new HashSet<>(Arrays.asList(TableDescriptorBuilder.REGION_REPLICATION, 146 TableDescriptorBuilder.REGION_MEMSTORE_REPLICATION, RSGroupInfo.TABLE_DESC_PROP_GROUP)); 147 for (String k : s) { 148 if ( 149 isTablePropertyModified(this.unmodifiedTableDescriptor, this.modifiedTableDescriptor, k) 150 ) { 151 throw new HBaseIOException( 152 "Can not modify " + k + " of a table when modification won't reopen regions"); 153 } 154 } 155 } 156 } 157 158 /** 159 * Comparing the value associated with a given key across two TableDescriptor instances' 160 * properties. 161 * @return True if the table property <code>key</code> is the same in both. 162 */ 163 private boolean isTablePropertyModified(TableDescriptor oldDescriptor, 164 TableDescriptor newDescriptor, String key) { 165 String oldV = oldDescriptor.getValue(key); 166 String newV = newDescriptor.getValue(key); 167 if (oldV == null && newV == null) { 168 return false; 169 } else if (oldV != null && newV != null && oldV.equals(newV)) { 170 return false; 171 } 172 return true; 173 } 174 175 private void initialize(final TableDescriptor unmodifiedTableDescriptor, 176 final boolean shouldCheckDescriptor) { 177 this.unmodifiedTableDescriptor = unmodifiedTableDescriptor; 178 this.shouldCheckDescriptor = shouldCheckDescriptor; 179 this.deleteColumnFamilyInModify = false; 180 } 181 182 @Override 183 protected Flow executeFromState(final MasterProcedureEnv env, final ModifyTableState state) 184 throws InterruptedException { 185 LOG.trace("{} execute state={}", this, state); 186 try { 187 switch (state) { 188 case MODIFY_TABLE_PREPARE: 189 prepareModify(env); 190 setNextState(ModifyTableState.MODIFY_TABLE_PRE_OPERATION); 191 break; 192 case MODIFY_TABLE_PRE_OPERATION: 193 preModify(env, state); 194 // We cannot allow changes to region replicas when 'reopenRegions==false', 195 // as this mode bypasses the state management required for modifying region replicas. 196 if (reopenRegions) { 197 setNextState(ModifyTableState.MODIFY_TABLE_CLOSE_EXCESS_REPLICAS); 198 } else { 199 setNextState(ModifyTableState.MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR); 200 } 201 break; 202 case MODIFY_TABLE_CLOSE_EXCESS_REPLICAS: 203 if (isTableEnabled(env)) { 204 closeExcessReplicasIfNeeded(env); 205 } 206 setNextState(ModifyTableState.MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR); 207 break; 208 case MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR: 209 updateTableDescriptor(env); 210 if (reopenRegions) { 211 setNextState(ModifyTableState.MODIFY_TABLE_REMOVE_REPLICA_COLUMN); 212 } else { 213 setNextState(ModifyTableState.MODIFY_TABLE_POST_OPERATION); 214 } 215 break; 216 case MODIFY_TABLE_REMOVE_REPLICA_COLUMN: 217 removeReplicaColumnsIfNeeded(env); 218 setNextState(ModifyTableState.MODIFY_TABLE_POST_OPERATION); 219 break; 220 case MODIFY_TABLE_POST_OPERATION: 221 postModify(env, state); 222 if (reopenRegions) { 223 setNextState(ModifyTableState.MODIFY_TABLE_REOPEN_ALL_REGIONS); 224 } else 225 if (ErasureCodingUtils.needsSync(unmodifiedTableDescriptor, modifiedTableDescriptor)) { 226 setNextState(ModifyTableState.MODIFY_TABLE_SYNC_ERASURE_CODING_POLICY); 227 } else { 228 return Flow.NO_MORE_STATE; 229 } 230 break; 231 case MODIFY_TABLE_REOPEN_ALL_REGIONS: 232 if (isTableEnabled(env)) { 233 Configuration conf = env.getMasterConfiguration(); 234 long backoffMillis = conf.getLong(PROGRESSIVE_BATCH_BACKOFF_MILLIS_KEY, 235 PROGRESSIVE_BATCH_BACKOFF_MILLIS_DEFAULT); 236 int batchSizeMax = 237 conf.getInt(PROGRESSIVE_BATCH_SIZE_MAX_KEY, PROGRESSIVE_BATCH_SIZE_MAX_DISABLED); 238 addChildProcedure( 239 new ReopenTableRegionsProcedure(getTableName(), backoffMillis, batchSizeMax)); 240 } 241 setNextState(ModifyTableState.MODIFY_TABLE_ASSIGN_NEW_REPLICAS); 242 break; 243 case MODIFY_TABLE_ASSIGN_NEW_REPLICAS: 244 assignNewReplicasIfNeeded(env); 245 if (TableName.isMetaTableName(getTableName())) { 246 MetaLocationSyncer syncer = env.getMasterServices().getMetaLocationSyncer(); 247 if (syncer != null) { 248 syncer.setMetaReplicaCount(modifiedTableDescriptor.getRegionReplication()); 249 } 250 } 251 if (deleteColumnFamilyInModify) { 252 setNextState(ModifyTableState.MODIFY_TABLE_DELETE_FS_LAYOUT); 253 } else 254 if (ErasureCodingUtils.needsSync(unmodifiedTableDescriptor, modifiedTableDescriptor)) { 255 setNextState(ModifyTableState.MODIFY_TABLE_SYNC_ERASURE_CODING_POLICY); 256 } else { 257 return Flow.NO_MORE_STATE; 258 } 259 break; 260 case MODIFY_TABLE_DELETE_FS_LAYOUT: 261 deleteFromFs(env, unmodifiedTableDescriptor, modifiedTableDescriptor); 262 if (ErasureCodingUtils.needsSync(unmodifiedTableDescriptor, modifiedTableDescriptor)) { 263 setNextState(ModifyTableState.MODIFY_TABLE_SYNC_ERASURE_CODING_POLICY); 264 break; 265 } else { 266 return Flow.NO_MORE_STATE; 267 } 268 case MODIFY_TABLE_SYNC_ERASURE_CODING_POLICY: 269 ErasureCodingUtils.sync(env.getMasterFileSystem().getFileSystem(), 270 env.getMasterFileSystem().getRootDir(), modifiedTableDescriptor); 271 return Flow.NO_MORE_STATE; 272 default: 273 throw new UnsupportedOperationException("unhandled state=" + state); 274 } 275 } catch (IOException e) { 276 if (isRollbackSupported(state)) { 277 setFailure("master-modify-table", e); 278 } else { 279 LOG.warn("Retriable error trying to modify table={} (in state={})", getTableName(), state, 280 e); 281 } 282 } 283 return Flow.HAS_MORE_STATE; 284 } 285 286 @Override 287 protected void rollbackState(final MasterProcedureEnv env, final ModifyTableState state) 288 throws IOException { 289 if ( 290 state == ModifyTableState.MODIFY_TABLE_PREPARE 291 || state == ModifyTableState.MODIFY_TABLE_PRE_OPERATION 292 ) { 293 // nothing to rollback, pre-modify is just checks. 294 // TODO: coprocessor rollback semantic is still undefined. 295 return; 296 } 297 298 // The delete doesn't have a rollback. The execution will succeed, at some point. 299 throw new UnsupportedOperationException("unhandled state=" + state); 300 } 301 302 @Override 303 protected boolean isRollbackSupported(final ModifyTableState state) { 304 switch (state) { 305 case MODIFY_TABLE_PRE_OPERATION: 306 case MODIFY_TABLE_PREPARE: 307 return true; 308 default: 309 return false; 310 } 311 } 312 313 @Override 314 protected void completionCleanup(final MasterProcedureEnv env) { 315 releaseSyncLatch(); 316 } 317 318 @Override 319 protected ModifyTableState getState(final int stateId) { 320 return ModifyTableState.forNumber(stateId); 321 } 322 323 @Override 324 protected int getStateId(final ModifyTableState state) { 325 return state.getNumber(); 326 } 327 328 @Override 329 protected ModifyTableState getInitialState() { 330 return ModifyTableState.MODIFY_TABLE_PREPARE; 331 } 332 333 @Override 334 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 335 super.serializeStateData(serializer); 336 337 MasterProcedureProtos.ModifyTableStateData.Builder modifyTableMsg = 338 MasterProcedureProtos.ModifyTableStateData.newBuilder() 339 .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser())) 340 .setModifiedTableSchema(ProtobufUtil.toTableSchema(modifiedTableDescriptor)) 341 .setDeleteColumnFamilyInModify(deleteColumnFamilyInModify) 342 .setShouldCheckDescriptor(shouldCheckDescriptor).setReopenRegions(reopenRegions); 343 344 if (unmodifiedTableDescriptor != null) { 345 modifyTableMsg 346 .setUnmodifiedTableSchema(ProtobufUtil.toTableSchema(unmodifiedTableDescriptor)); 347 } 348 349 serializer.serialize(modifyTableMsg.build()); 350 } 351 352 @Override 353 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 354 super.deserializeStateData(serializer); 355 356 MasterProcedureProtos.ModifyTableStateData modifyTableMsg = 357 serializer.deserialize(MasterProcedureProtos.ModifyTableStateData.class); 358 setUser(MasterProcedureUtil.toUserInfo(modifyTableMsg.getUserInfo())); 359 modifiedTableDescriptor = 360 ProtobufUtil.toTableDescriptor(modifyTableMsg.getModifiedTableSchema()); 361 deleteColumnFamilyInModify = modifyTableMsg.getDeleteColumnFamilyInModify(); 362 shouldCheckDescriptor = 363 modifyTableMsg.hasShouldCheckDescriptor() ? modifyTableMsg.getShouldCheckDescriptor() : false; 364 reopenRegions = modifyTableMsg.hasReopenRegions() ? modifyTableMsg.getReopenRegions() : true; 365 366 if (modifyTableMsg.hasUnmodifiedTableSchema()) { 367 unmodifiedTableDescriptor = 368 ProtobufUtil.toTableDescriptor(modifyTableMsg.getUnmodifiedTableSchema()); 369 } 370 } 371 372 @Override 373 public TableName getTableName() { 374 return modifiedTableDescriptor.getTableName(); 375 } 376 377 @Override 378 public TableOperationType getTableOperationType() { 379 return TableOperationType.EDIT; 380 } 381 382 /** 383 * Check conditions before any real action of modifying a table. 384 */ 385 private void prepareModify(final MasterProcedureEnv env) throws IOException { 386 // Checks whether the table exists 387 if (!env.getMasterServices().getTableDescriptors().exists(getTableName())) { 388 throw new TableNotFoundException(getTableName()); 389 } 390 391 // check that we have at least 1 CF 392 if (modifiedTableDescriptor.getColumnFamilyCount() == 0) { 393 throw new DoNotRetryIOException( 394 "Table " + getTableName().toString() + " should have at least one column family."); 395 } 396 397 // If descriptor check is enabled, check whether the table descriptor when procedure was 398 // submitted matches with the current 399 // table descriptor of the table, else retrieve the old descriptor 400 // for comparison in order to update the descriptor. 401 if (shouldCheckDescriptor) { 402 if ( 403 TableDescriptor.COMPARATOR.compare(unmodifiedTableDescriptor, 404 env.getMasterServices().getTableDescriptors().get(getTableName())) != 0 405 ) { 406 LOG.error("Error while modifying table '" + getTableName().toString() 407 + "' Skipping procedure : " + this); 408 throw new ConcurrentTableModificationException( 409 "Skipping modify table operation on table '" + getTableName().toString() 410 + "' as it has already been modified by some other concurrent operation, " 411 + "Please retry."); 412 } 413 } else { 414 this.unmodifiedTableDescriptor = 415 env.getMasterServices().getTableDescriptors().get(getTableName()); 416 } 417 418 this.deleteColumnFamilyInModify = 419 isDeleteColumnFamily(unmodifiedTableDescriptor, modifiedTableDescriptor); 420 if ( 421 !unmodifiedTableDescriptor.getRegionServerGroup() 422 .equals(modifiedTableDescriptor.getRegionServerGroup()) 423 ) { 424 Supplier<String> forWhom = () -> "table " + getTableName(); 425 RSGroupInfo rsGroupInfo = MasterProcedureUtil.checkGroupExists( 426 env.getMasterServices().getRSGroupInfoManager()::getRSGroup, 427 modifiedTableDescriptor.getRegionServerGroup(), forWhom); 428 MasterProcedureUtil.checkGroupNotEmpty(rsGroupInfo, forWhom); 429 } 430 431 // check for store file tracker configurations 432 StoreFileTrackerValidationUtils.checkForModifyTable(env.getMasterConfiguration(), 433 unmodifiedTableDescriptor, modifiedTableDescriptor, !isTableEnabled(env)); 434 } 435 436 /** 437 * Find out whether all column families in unmodifiedTableDescriptor also exists in the 438 * modifiedTableDescriptor. 439 * @return True if we are deleting a column family. 440 */ 441 private static boolean isDeleteColumnFamily(TableDescriptor originalDescriptor, 442 TableDescriptor newDescriptor) { 443 boolean result = false; 444 final Set<byte[]> originalFamilies = originalDescriptor.getColumnFamilyNames(); 445 final Set<byte[]> newFamilies = newDescriptor.getColumnFamilyNames(); 446 for (byte[] familyName : originalFamilies) { 447 if (!newFamilies.contains(familyName)) { 448 result = true; 449 break; 450 } 451 } 452 return result; 453 } 454 455 /** 456 * Action before modifying table. 457 * @param env MasterProcedureEnv 458 * @param state the procedure state 459 */ 460 private void preModify(final MasterProcedureEnv env, final ModifyTableState state) 461 throws IOException, InterruptedException { 462 runCoprocessorAction(env, state); 463 } 464 465 /** 466 * Update descriptor 467 * @param env MasterProcedureEnv 468 **/ 469 private void updateTableDescriptor(final MasterProcedureEnv env) throws IOException { 470 env.getMasterServices().getTableDescriptors().update(modifiedTableDescriptor); 471 } 472 473 /** 474 * Removes from hdfs the families that are not longer present in the new table descriptor. 475 * @param env MasterProcedureEnv 476 */ 477 private void deleteFromFs(final MasterProcedureEnv env, final TableDescriptor oldTableDescriptor, 478 final TableDescriptor newTableDescriptor) throws IOException { 479 final Set<byte[]> oldFamilies = oldTableDescriptor.getColumnFamilyNames(); 480 final Set<byte[]> newFamilies = newTableDescriptor.getColumnFamilyNames(); 481 for (byte[] familyName : oldFamilies) { 482 if (!newFamilies.contains(familyName)) { 483 MasterDDLOperationHelper.deleteColumnFamilyFromFileSystem(env, getTableName(), 484 getRegionInfoList(env), familyName, 485 oldTableDescriptor.getColumnFamily(familyName).isMobEnabled()); 486 } 487 } 488 } 489 490 /** 491 * remove replica columns if necessary. 492 */ 493 private void removeReplicaColumnsIfNeeded(MasterProcedureEnv env) throws IOException { 494 final int oldReplicaCount = unmodifiedTableDescriptor.getRegionReplication(); 495 final int newReplicaCount = modifiedTableDescriptor.getRegionReplication(); 496 if (newReplicaCount >= oldReplicaCount) { 497 return; 498 } 499 env.getAssignmentManager().getRegionStateStore().removeRegionReplicas(getTableName(), 500 oldReplicaCount, newReplicaCount); 501 env.getAssignmentManager().getRegionStates().getRegionsOfTable(getTableName()).stream() 502 .filter(r -> r.getReplicaId() >= newReplicaCount) 503 .forEach(env.getAssignmentManager().getRegionStates()::deleteRegion); 504 } 505 506 private void assignNewReplicasIfNeeded(MasterProcedureEnv env) throws IOException { 507 final int oldReplicaCount = unmodifiedTableDescriptor.getRegionReplication(); 508 final int newReplicaCount = modifiedTableDescriptor.getRegionReplication(); 509 if (newReplicaCount <= oldReplicaCount) { 510 return; 511 } 512 if (isTableEnabled(env)) { 513 List<RegionInfo> newReplicas = env.getAssignmentManager().getRegionStates() 514 .getRegionsOfTable(getTableName()).stream().filter(RegionReplicaUtil::isDefaultReplica) 515 .flatMap(primaryRegion -> IntStream.range(oldReplicaCount, newReplicaCount).mapToObj( 516 replicaId -> RegionReplicaUtil.getRegionInfoForReplica(primaryRegion, replicaId))) 517 .collect(Collectors.toList()); 518 addChildProcedure(env.getAssignmentManager().createAssignProcedures(newReplicas)); 519 } 520 } 521 522 private void closeExcessReplicasIfNeeded(MasterProcedureEnv env) { 523 final int oldReplicaCount = unmodifiedTableDescriptor.getRegionReplication(); 524 final int newReplicaCount = modifiedTableDescriptor.getRegionReplication(); 525 if (newReplicaCount >= oldReplicaCount) { 526 return; 527 } 528 addChildProcedure(new CloseExcessRegionReplicasProcedure(getTableName(), newReplicaCount)); 529 } 530 531 /** 532 * Action after modifying table. 533 * @param env MasterProcedureEnv 534 * @param state the procedure state 535 */ 536 private void postModify(final MasterProcedureEnv env, final ModifyTableState state) 537 throws IOException, InterruptedException { 538 runCoprocessorAction(env, state); 539 } 540 541 /** 542 * Coprocessor Action. 543 * @param env MasterProcedureEnv 544 * @param state the procedure state 545 */ 546 private void runCoprocessorAction(final MasterProcedureEnv env, final ModifyTableState state) 547 throws IOException, InterruptedException { 548 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); 549 if (cpHost != null) { 550 switch (state) { 551 case MODIFY_TABLE_PRE_OPERATION: 552 cpHost.preModifyTableAction(getTableName(), unmodifiedTableDescriptor, 553 modifiedTableDescriptor, getUser()); 554 break; 555 case MODIFY_TABLE_POST_OPERATION: 556 cpHost.postCompletedModifyTableAction(getTableName(), unmodifiedTableDescriptor, 557 modifiedTableDescriptor, getUser()); 558 break; 559 default: 560 throw new UnsupportedOperationException(this + " unhandled state=" + state); 561 } 562 } 563 } 564 565 /** 566 * Fetches all Regions for a table. Cache the result of this method if you need to use it multiple 567 * times. Be aware that it may change over in between calls to this procedure. 568 */ 569 private List<RegionInfo> getRegionInfoList(final MasterProcedureEnv env) throws IOException { 570 return env.getAssignmentManager().getRegionStates().getRegionsOfTable(getTableName()); 571 } 572}