3232import org .apache .iotdb .confignode .procedure .env .ConfigNodeProcedureEnv ;
3333import org .apache .iotdb .confignode .procedure .env .RegionMaintainHandler ;
3434import org .apache .iotdb .confignode .procedure .exception .ProcedureException ;
35+ import org .apache .iotdb .confignode .procedure .state .ProcedureState ;
3536import org .apache .iotdb .confignode .procedure .state .RemoveRegionPeerState ;
3637import org .apache .iotdb .confignode .procedure .store .ProcedureType ;
3738import org .apache .iotdb .mpp .rpc .thrift .TRegionMigrateResult ;
@@ -55,6 +56,7 @@ public class RemoveRegionPeerProcedure extends RegionOperationProcedure<RemoveRe
5556 private static final Logger LOGGER = LoggerFactory .getLogger (RemoveRegionPeerProcedure .class );
5657 private static final int MAX_DELETE_OLD_REGION_PEER_RETRY = 3 ;
5758 private static final long DELETE_OLD_REGION_PEER_RETRY_INTERVAL_MS = 5_000 ;
59+ private static final long RECONCILE_PEER_LIST_RETRY_INTERVAL_MS = 5_000 ;
5860 private TDataNodeLocation coordinator ;
5961 private TDataNodeLocation targetDataNode ;
6062 private transient int deleteOldRegionPeerAttempted = 0 ;
@@ -91,6 +93,8 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, RemoveRegionPeerStat
9193 }
9294 TSStatus tsStatus ;
9395 RegionMaintainHandler handler = env .getRegionMaintainHandler ();
96+ final boolean reconcilePeerList =
97+ state == REMOVE_REGION_LOCATION_CACHE && handler .isIoTConsensusDataRegion (regionId );
9498 try {
9599 switch (state ) {
96100 case TRANSFER_REGION_LEADER :
@@ -193,7 +197,13 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, RemoveRegionPeerStat
193197 setNextState (REMOVE_REGION_LOCATION_CACHE );
194198 break ;
195199 case REMOVE_REGION_LOCATION_CACHE :
196- handler .removeRegionLocation (regionId , targetDataNode );
200+ tsStatus = handler .removeRegionLocation (regionId , targetDataNode );
201+ if (reconcilePeerList
202+ && (tsStatus == null
203+ || tsStatus .getCode () != SUCCESS_STATUS .getStatusCode ()
204+ || !handler .resetPeerListAfterRemove (regionId , targetDataNode ))) {
205+ return suspendFinalizationForRetry ();
206+ }
197207 setKillPoint (state );
198208 LOGGER .info (ProcedureMessages .REMOVEREGIONPEER_STATE_SUCCESS , state );
199209 LOGGER .info (
@@ -211,12 +221,40 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, RemoveRegionPeerStat
211221 }
212222 } catch (Exception e ) {
213223 LOGGER .error (ProcedureMessages .REMOVEREGIONPEER_STATE_FAILED , state , e );
224+ if (reconcilePeerList ) {
225+ return suspendFinalizationForRetry ();
226+ }
214227 return Flow .NO_MORE_STATE ;
215228 }
216229 LOGGER .info (ProcedureMessages .PID_REMOVEREGION_STATE_SUCCESS , getProcId (), state );
217230 return Flow .HAS_MORE_STATE ;
218231 }
219232
233+ private Flow suspendFinalizationForRetry () {
234+ // Keep the existing persisted state until every retained replica acknowledges the final list.
235+ // Replaying the metadata removal after a leader switch is idempotent.
236+ setNextState (REMOVE_REGION_LOCATION_CACHE );
237+ setTimeout (RECONCILE_PEER_LIST_RETRY_INTERVAL_MS );
238+ setState (ProcedureState .WAITING_TIMEOUT );
239+ LOGGER .warn (
240+ ProcedureMessages .LOG_PIDARG_REMOVEREGION_STATE_ARG_WILL_RETRY_IN_ARG_MS_080917A9 ,
241+ getProcId (),
242+ REMOVE_REGION_LOCATION_CACHE ,
243+ RECONCILE_PEER_LIST_RETRY_INTERVAL_MS );
244+ return Flow .HAS_MORE_STATE ;
245+ }
246+
247+ @ Override
248+ protected synchronized boolean setTimeoutFailure (ConfigNodeProcedureEnv env ) {
249+ if (getState () == ProcedureState .WAITING_TIMEOUT
250+ && getCurrentState () == REMOVE_REGION_LOCATION_CACHE ) {
251+ setState (ProcedureState .RUNNABLE );
252+ env .getScheduler ().addBack (this );
253+ return false ;
254+ }
255+ return super .setTimeoutFailure (env );
256+ }
257+
220258 @ Override
221259 protected void rollbackState (ConfigNodeProcedureEnv env , RemoveRegionPeerState state )
222260 throws IOException , InterruptedException , ProcedureException {}
0 commit comments