@@ -515,31 +515,37 @@ public IndexShardState markAsRecovering(String reason, RecoveryState recoverySta
515515
516516 private final AtomicBoolean primaryReplicaResyncInProgress = new AtomicBoolean ();
517517
518- public void relocated (String reason ) throws IllegalIndexShardStateException , InterruptedException {
518+ /**
519+ * Completes the relocation. Operations are blocked and current operations are drained before changing state to relocated. The provided
520+ * {@link Runnable} is executed after all operations are successfully blocked.
521+ *
522+ * @param reason the reason for the relocation
523+ * @param consumer a {@link Runnable} that is executed after operations are blocked
524+ * @throws IllegalIndexShardStateException if the shard is not relocating due to concurrent cancellation
525+ * @throws InterruptedException if blocking operations is interrupted
526+ */
527+ public void relocated (
528+ final String reason , final Consumer <PrimaryContext > consumer ) throws IllegalIndexShardStateException , InterruptedException {
519529 assert shardRouting .primary () : "only primaries can be marked as relocated: " + shardRouting ;
520530 try {
521531 indexShardOperationPermits .blockOperations (30 , TimeUnit .MINUTES , () -> {
522532 // no shard operation permits are being held here, move state from started to relocated
523533 assert indexShardOperationPermits .getActiveOperationsCount () == 0 :
524- "in-flight operations in progress while moving shard state to relocated" ;
525- synchronized (mutex ) {
526- if (state != IndexShardState .STARTED ) {
527- throw new IndexShardNotStartedException (shardId , state );
528- }
529- // if the master cancelled the recovery, the target will be removed
530- // and the recovery will stopped.
531- // However, it is still possible that we concurrently end up here
532- // and therefore have to protect we don't mark the shard as relocated when
533- // its shard routing says otherwise.
534- if (shardRouting .relocating () == false ) {
535- throw new IllegalIndexShardStateException (shardId , IndexShardState .STARTED ,
536- ": shard is no longer relocating " + shardRouting );
537- }
538- if (primaryReplicaResyncInProgress .get ()) {
539- throw new IllegalIndexShardStateException (shardId , IndexShardState .STARTED ,
540- ": primary relocation is forbidden while primary-replica resync is in progress " + shardRouting );
534+ "in-flight operations in progress while moving shard state to relocated" ;
535+ /*
536+ * We should not invoke the runnable under the mutex as the expected implementation is to handoff the primary context via a
537+ * network operation. Doing this under the mutex can implicitly block the cluster state update thread on network operations.
538+ */
539+ verifyRelocatingState ();
540+ final PrimaryContext primaryContext = getEngine ().seqNoService ().primaryContext ();
541+ try {
542+ consumer .accept (primaryContext );
543+ synchronized (mutex ) {
544+ verifyRelocatingState ();
545+ changeState (IndexShardState .RELOCATED , reason );
541546 }
542- changeState (IndexShardState .RELOCATED , reason );
547+ } catch (final Exception e ) {
548+ getEngine ().seqNoService ().releasePrimaryContext ();
543549 }
544550 });
545551 } catch (TimeoutException e ) {
@@ -551,6 +557,26 @@ public void relocated(String reason) throws IllegalIndexShardStateException, Int
551557 }
552558 }
553559
560+ private void verifyRelocatingState () {
561+ if (state != IndexShardState .STARTED ) {
562+ throw new IndexShardNotStartedException (shardId , state );
563+ }
564+ /*
565+ * If the master cancelled recovery, the target will be removed and the recovery will be cancelled. However, it is still possible
566+ * that we concurrently end up here and therefore have to protect that we do not mark the shard as relocated when its shard routing
567+ * says otherwise.
568+ */
569+
570+ if (shardRouting .relocating () == false ) {
571+ throw new IllegalIndexShardStateException (shardId , IndexShardState .STARTED ,
572+ ": shard is no longer relocating " + shardRouting );
573+ }
574+
575+ if (primaryReplicaResyncInProgress .get ()) {
576+ throw new IllegalIndexShardStateException (shardId , IndexShardState .STARTED ,
577+ ": primary relocation is forbidden while primary-replica resync is in progress " + shardRouting );
578+ }
579+ }
554580
555581 public IndexShardState state () {
556582 return state ;
@@ -1319,16 +1345,16 @@ private void ensureWriteAllowed(Engine.Operation.Origin origin) throws IllegalIn
13191345
13201346 private void verifyPrimary () {
13211347 if (shardRouting .primary () == false ) {
1322- throw new IllegalStateException ("shard is not a primary " + shardRouting );
1348+ throw new IllegalStateException ("shard " + shardRouting + " is not a primary" );
13231349 }
13241350 }
13251351
13261352 private void verifyReplicationTarget () {
13271353 final IndexShardState state = state ();
13281354 if (shardRouting .primary () && shardRouting .active () && state != IndexShardState .RELOCATED ) {
13291355 // must use exception that is not ignored by replication logic. See TransportActions.isShardNotAvailableException
1330- throw new IllegalStateException ("active primary shard cannot be a replication target before " +
1331- " relocation hand off " + shardRouting + " , state is [" + state + "]" );
1356+ throw new IllegalStateException ("active primary shard " + shardRouting + " cannot be a replication target before " +
1357+ "relocation hand off, state is [" + state + "]" );
13321358 }
13331359 }
13341360
@@ -1603,8 +1629,8 @@ public void markAllocationIdAsInSync(final String allocationId, final long local
16031629 verifyPrimary ();
16041630 getEngine ().seqNoService ().markAllocationIdAsInSync (allocationId , localCheckpoint );
16051631 /*
1606- * We could have blocked waiting for the replica to catch up that we fell idle and there will not be a background sync to the
1607- * replica; mark our self as active to force a future background sync.
1632+ * We could have blocked so long waiting for the replica to catch up that we fell idle and there will not be a background sync to
1633+ * the replica; mark our self as active to force a future background sync.
16081634 */
16091635 active .compareAndSet (false , true );
16101636 }
@@ -1654,18 +1680,34 @@ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) {
16541680
16551681 /**
16561682 * Notifies the service of the current allocation IDs in the cluster state. See
1657- * {@link org.elasticsearch.index.seqno.GlobalCheckpointTracker#updateAllocationIdsFromMaster(Set, Set)}
1683+ * {@link org.elasticsearch.index.seqno.GlobalCheckpointTracker#updateAllocationIdsFromMaster(long, Set, Set)}
16581684 * for details.
16591685 *
1660- * @param activeAllocationIds the allocation IDs of the currently active shard copies
1661- * @param initializingAllocationIds the allocation IDs of the currently initializing shard copies
1686+ * @param applyingClusterStateVersion the cluster state version being applied when updating the allocation IDs from the master
1687+ * @param activeAllocationIds the allocation IDs of the currently active shard copies
1688+ * @param initializingAllocationIds the allocation IDs of the currently initializing shard copies
16621689 */
1663- public void updateAllocationIdsFromMaster (final Set <String > activeAllocationIds , final Set <String > initializingAllocationIds ) {
1690+ public void updateAllocationIdsFromMaster (
1691+ final long applyingClusterStateVersion , final Set <String > activeAllocationIds , final Set <String > initializingAllocationIds ) {
16641692 verifyPrimary ();
16651693 final Engine engine = getEngineOrNull ();
16661694 // if the engine is not yet started, we are not ready yet and can just ignore this
16671695 if (engine != null ) {
1668- engine .seqNoService ().updateAllocationIdsFromMaster (activeAllocationIds , initializingAllocationIds );
1696+ engine .seqNoService ().updateAllocationIdsFromMaster (applyingClusterStateVersion , activeAllocationIds , initializingAllocationIds );
1697+ }
1698+ }
1699+
1700+ /**
1701+ * Updates the known allocation IDs and the local checkpoints for the corresponding allocations from a primary relocation source.
1702+ *
1703+ * @param primaryContext the sequence number context
1704+ */
1705+ public void updateAllocationIdsFromPrimaryContext (final PrimaryContext primaryContext ) {
1706+ verifyPrimary ();
1707+ assert shardRouting .isRelocationTarget () : "only relocation target can update allocation IDs from primary context: " + shardRouting ;
1708+ final Engine engine = getEngineOrNull ();
1709+ if (engine != null ) {
1710+ engine .seqNoService ().updateAllocationIdsFromPrimaryContext (primaryContext );
16691711 }
16701712 }
16711713
0 commit comments