2525import org .elasticsearch .ElasticsearchException ;
2626import org .elasticsearch .ExceptionsHelper ;
2727import org .elasticsearch .Version ;
28+ import org .elasticsearch .action .ActionListener ;
2829import org .elasticsearch .cluster .ClusterChangedEvent ;
2930import org .elasticsearch .cluster .ClusterState ;
3031import org .elasticsearch .cluster .ClusterStateObserver ;
4849import org .elasticsearch .common .io .stream .StreamInput ;
4950import org .elasticsearch .common .io .stream .StreamOutput ;
5051import org .elasticsearch .common .unit .TimeValue ;
51- import org .elasticsearch .common .util .concurrent .ConcurrentCollections ;
5252import org .elasticsearch .index .shard .ShardId ;
5353import org .elasticsearch .node .NodeClosedException ;
5454import org .elasticsearch .tasks .Task ;
5555import org .elasticsearch .threadpool .ThreadPool ;
5656import org .elasticsearch .transport .ConnectTransportException ;
5757import org .elasticsearch .transport .EmptyTransportResponseHandler ;
58- import org .elasticsearch .transport .NodeDisconnectedException ;
5958import org .elasticsearch .transport .RemoteTransportException ;
6059import org .elasticsearch .transport .TransportChannel ;
6160import org .elasticsearch .transport .TransportException ;
6261import org .elasticsearch .transport .TransportRequest ;
62+ import org .elasticsearch .transport .TransportRequestDeduplicator ;
6363import org .elasticsearch .transport .TransportRequestHandler ;
6464import org .elasticsearch .transport .TransportResponse ;
6565import org .elasticsearch .transport .TransportService ;
7171import java .util .Locale ;
7272import java .util .Objects ;
7373import java .util .Set ;
74- import java .util .concurrent .ConcurrentMap ;
7574import java .util .function .Predicate ;
7675
7776import static org .elasticsearch .index .seqno .SequenceNumbers .UNASSIGNED_PRIMARY_TERM ;
@@ -89,7 +88,7 @@ public class ShardStateAction {
8988
9089 // a list of shards that failed during replication
9190 // we keep track of these shards in order to avoid sending duplicate failed shard requests for a single failing shard.
92- private final ConcurrentMap <FailedShardEntry , CompositeListener > remoteFailedShardsCache = ConcurrentCollections . newConcurrentMap ();
91+ private final TransportRequestDeduplicator <FailedShardEntry > remoteFailedShardsDeduplicator = new TransportRequestDeduplicator <> ();
9392
9493 @ Inject
9594 public ShardStateAction (ClusterService clusterService , TransportService transportService ,
@@ -106,7 +105,7 @@ public ShardStateAction(ClusterService clusterService, TransportService transpor
106105 }
107106
108107 private void sendShardAction (final String actionName , final ClusterState currentState ,
109- final TransportRequest request , final Listener listener ) {
108+ final TransportRequest request , final ActionListener < Void > listener ) {
110109 ClusterStateObserver observer =
111110 new ClusterStateObserver (currentState , clusterService , null , logger , threadPool .getThreadContext ());
112111 DiscoveryNode masterNode = currentState .nodes ().getMasterNode ();
@@ -120,7 +119,7 @@ private void sendShardAction(final String actionName, final ClusterState current
120119 actionName , request , new EmptyTransportResponseHandler (ThreadPool .Names .SAME ) {
121120 @ Override
122121 public void handleResponse (TransportResponse .Empty response ) {
123- listener .onSuccess ( );
122+ listener .onResponse ( null );
124123 }
125124
126125 @ Override
@@ -163,60 +162,39 @@ private static boolean isMasterChannelException(TransportException exp) {
163162 * @param listener callback upon completion of the request
164163 */
165164 public void remoteShardFailed (final ShardId shardId , String allocationId , long primaryTerm , boolean markAsStale , final String message ,
166- @ Nullable final Exception failure , Listener listener ) {
165+ @ Nullable final Exception failure , ActionListener < Void > listener ) {
167166 assert primaryTerm > 0L : "primary term should be strictly positive" ;
168- final FailedShardEntry shardEntry = new FailedShardEntry (shardId , allocationId , primaryTerm , message , failure , markAsStale );
169- final CompositeListener compositeListener = new CompositeListener (listener );
170- final CompositeListener existingListener = remoteFailedShardsCache .putIfAbsent (shardEntry , compositeListener );
171- if (existingListener == null ) {
172- sendShardAction (SHARD_FAILED_ACTION_NAME , clusterService .state (), shardEntry , new Listener () {
173- @ Override
174- public void onSuccess () {
175- try {
176- compositeListener .onSuccess ();
177- } finally {
178- remoteFailedShardsCache .remove (shardEntry );
179- }
180- }
181- @ Override
182- public void onFailure (Exception e ) {
183- try {
184- compositeListener .onFailure (e );
185- } finally {
186- remoteFailedShardsCache .remove (shardEntry );
187- }
188- }
189- });
190- } else {
191- existingListener .addListener (listener );
192- }
167+ remoteFailedShardsDeduplicator .executeOnce (
168+ new FailedShardEntry (shardId , allocationId , primaryTerm , message , failure , markAsStale ), listener ,
169+ (req , reqListener ) -> sendShardAction (SHARD_FAILED_ACTION_NAME , clusterService .state (), req , reqListener ));
193170 }
194171
195172 int remoteShardFailedCacheSize () {
196- return remoteFailedShardsCache .size ();
173+ return remoteFailedShardsDeduplicator .size ();
197174 }
198175
199176 /**
200177 * Send a shard failed request to the master node to update the cluster state when a shard on the local node failed.
201178 */
202179 public void localShardFailed (final ShardRouting shardRouting , final String message ,
203- @ Nullable final Exception failure , Listener listener ) {
180+ @ Nullable final Exception failure , ActionListener < Void > listener ) {
204181 localShardFailed (shardRouting , message , failure , listener , clusterService .state ());
205182 }
206183
207184 /**
208185 * Send a shard failed request to the master node to update the cluster state when a shard on the local node failed.
209186 */
210187 public void localShardFailed (final ShardRouting shardRouting , final String message , @ Nullable final Exception failure ,
211- Listener listener , final ClusterState currentState ) {
188+ ActionListener < Void > listener , final ClusterState currentState ) {
212189 FailedShardEntry shardEntry = new FailedShardEntry (shardRouting .shardId (), shardRouting .allocationId ().getId (),
213190 0L , message , failure , true );
214191 sendShardAction (SHARD_FAILED_ACTION_NAME , currentState , shardEntry , listener );
215192 }
216193
217194 // visible for testing
218195 protected void waitForNewMasterAndRetry (String actionName , ClusterStateObserver observer ,
219- TransportRequest request , Listener listener , Predicate <ClusterState > changePredicate ) {
196+ TransportRequest request , ActionListener <Void > listener ,
197+ Predicate <ClusterState > changePredicate ) {
220198 observer .waitForNextChange (new ClusterStateObserver .Listener () {
221199 @ Override
222200 public void onNewClusterState (ClusterState state ) {
@@ -497,14 +475,14 @@ public int hashCode() {
497475 public void shardStarted (final ShardRouting shardRouting ,
498476 final long primaryTerm ,
499477 final String message ,
500- final Listener listener ) {
478+ final ActionListener < Void > listener ) {
501479 shardStarted (shardRouting , primaryTerm , message , listener , clusterService .state ());
502480 }
503481
504482 public void shardStarted (final ShardRouting shardRouting ,
505483 final long primaryTerm ,
506484 final String message ,
507- final Listener listener ,
485+ final ActionListener < Void > listener ,
508486 final ClusterState currentState ) {
509487 StartedShardEntry entry = new StartedShardEntry (shardRouting .shardId (), shardRouting .allocationId ().getId (), primaryTerm , message );
510488 sendShardAction (SHARD_STARTED_ACTION_NAME , currentState , entry , listener );
@@ -670,97 +648,6 @@ public String toString() {
670648 }
671649 }
672650
673- public interface Listener {
674-
675- default void onSuccess () {
676- }
677-
678- /**
679- * Notification for non-channel exceptions that are not handled
680- * by {@link ShardStateAction}.
681- *
682- * The exceptions that are handled by {@link ShardStateAction}
683- * are:
684- * - {@link NotMasterException}
685- * - {@link NodeDisconnectedException}
686- * - {@link FailedToCommitClusterStateException}
687- *
688- * Any other exception is communicated to the requester via
689- * this notification.
690- *
691- * @param e the unexpected cause of the failure on the master
692- */
693- default void onFailure (final Exception e ) {
694- }
695-
696- }
697-
698- /**
699- * A composite listener that allows registering multiple listeners dynamically.
700- */
701- static final class CompositeListener implements Listener {
702- private boolean isNotified = false ;
703- private Exception failure = null ;
704- private final List <Listener > listeners = new ArrayList <>();
705-
706- CompositeListener (Listener listener ) {
707- listeners .add (listener );
708- }
709-
710- void addListener (Listener listener ) {
711- final boolean ready ;
712- synchronized (this ) {
713- ready = this .isNotified ;
714- if (ready == false ) {
715- listeners .add (listener );
716- }
717- }
718- if (ready ) {
719- if (failure != null ) {
720- listener .onFailure (failure );
721- } else {
722- listener .onSuccess ();
723- }
724- }
725- }
726-
727- private void onCompleted (Exception failure ) {
728- synchronized (this ) {
729- this .failure = failure ;
730- this .isNotified = true ;
731- }
732- RuntimeException firstException = null ;
733- for (Listener listener : listeners ) {
734- try {
735- if (failure != null ) {
736- listener .onFailure (failure );
737- } else {
738- listener .onSuccess ();
739- }
740- } catch (RuntimeException innerEx ) {
741- if (firstException == null ) {
742- firstException = innerEx ;
743- } else {
744- firstException .addSuppressed (innerEx );
745- }
746- }
747- }
748- if (firstException != null ) {
749- throw firstException ;
750- }
751- }
752-
753- @ Override
754- public void onSuccess () {
755- onCompleted (null );
756- }
757-
758- @ Override
759- public void onFailure (Exception failure ) {
760- onCompleted (failure );
761- }
762- }
763-
764651 public static class NoLongerPrimaryShardException extends ElasticsearchException {
765652
766653 public NoLongerPrimaryShardException (ShardId shardId , String msg ) {
0 commit comments