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