22
22
import org .apache .logging .log4j .LogManager ;
23
23
import org .apache .logging .log4j .Logger ;
24
24
import org .apache .logging .log4j .message .ParameterizedMessage ;
25
+ import org .elasticsearch .ElasticsearchException ;
26
+ import org .elasticsearch .action .ActionListener ;
27
+ import org .elasticsearch .action .support .PlainListenableActionFuture ;
25
28
import org .elasticsearch .cluster .ClusterChangedEvent ;
26
29
import org .elasticsearch .cluster .ClusterState ;
27
30
import org .elasticsearch .cluster .ClusterStateUpdateTask ;
28
- import org .elasticsearch .cluster .routing . allocation . AllocationService ;
31
+ import org .elasticsearch .cluster .NotMasterException ;
29
32
import org .elasticsearch .cluster .service .ClusterService ;
33
+ import org .elasticsearch .common .Nullable ;
30
34
import org .elasticsearch .common .Priority ;
31
35
import org .elasticsearch .common .component .AbstractLifecycleComponent ;
32
36
import org .elasticsearch .common .inject .Inject ;
33
37
34
- import java .util .concurrent . atomic . AtomicBoolean ;
38
+ import java .util .function . BiFunction ;
35
39
36
40
/**
37
41
* A {@link RoutingService} listens to clusters state. When this service
@@ -51,14 +55,16 @@ public class RoutingService extends AbstractLifecycleComponent {
51
55
private static final String CLUSTER_UPDATE_TASK_SOURCE = "cluster_reroute" ;
52
56
53
57
private final ClusterService clusterService ;
54
- private final AllocationService allocationService ;
58
+ private final BiFunction < ClusterState , String , ClusterState > reroute ;
55
59
56
- private AtomicBoolean rerouting = new AtomicBoolean ();
60
+ private final Object mutex = new Object ();
61
+ @ Nullable // null if no reroute is currently pending
62
+ private PlainListenableActionFuture <Void > pendingRerouteListeners ;
57
63
58
64
@ Inject
59
- public RoutingService (ClusterService clusterService , AllocationService allocationService ) {
65
+ public RoutingService (ClusterService clusterService , BiFunction < ClusterState , String , ClusterState > reroute ) {
60
66
this .clusterService = clusterService ;
61
- this .allocationService = allocationService ;
67
+ this .reroute = reroute ;
62
68
}
63
69
64
70
@ Override
@@ -76,47 +82,78 @@ protected void doClose() {
76
82
/**
77
83
* Initiates a reroute.
78
84
*/
79
- public final void reroute (String reason ) {
80
- try {
81
- if (lifecycle .stopped ()) {
82
- return ;
83
- }
84
- if (rerouting .compareAndSet (false , true ) == false ) {
85
- logger .trace ("already has pending reroute, ignoring {}" , reason );
85
+ public final void reroute (String reason , ActionListener <Void > listener ) {
86
+ if (lifecycle .started () == false ) {
87
+ listener .onFailure (new IllegalStateException (
88
+ "rejecting delayed reroute [" + reason + "] in state [" + lifecycleState () + "]" ));
89
+ return ;
90
+ }
91
+ final PlainListenableActionFuture <Void > currentListeners ;
92
+ synchronized (mutex ) {
93
+ if (pendingRerouteListeners != null ) {
94
+ logger .trace ("already has pending reroute, adding [{}] to batch" , reason );
95
+ pendingRerouteListeners .addListener (listener );
86
96
return ;
87
97
}
88
- logger .trace ("rerouting {}" , reason );
98
+ currentListeners = PlainListenableActionFuture .newListenableFuture ();
99
+ currentListeners .addListener (listener );
100
+ pendingRerouteListeners = currentListeners ;
101
+ }
102
+ logger .trace ("rerouting [{}]" , reason );
103
+ try {
89
104
clusterService .submitStateUpdateTask (CLUSTER_UPDATE_TASK_SOURCE + "(" + reason + ")" ,
90
105
new ClusterStateUpdateTask (Priority .HIGH ) {
91
106
@ Override
92
107
public ClusterState execute (ClusterState currentState ) {
93
- rerouting .set (false );
94
- return allocationService .reroute (currentState , reason );
108
+ synchronized (mutex ) {
109
+ assert pendingRerouteListeners == currentListeners ;
110
+ pendingRerouteListeners = null ;
111
+ }
112
+ return reroute .apply (currentState , reason );
95
113
}
96
114
97
115
@ Override
98
116
public void onNoLongerMaster (String source ) {
99
- rerouting .set (false );
100
- // no biggie
117
+ synchronized (mutex ) {
118
+ if (pendingRerouteListeners == currentListeners ) {
119
+ pendingRerouteListeners = null ;
120
+ }
121
+ }
122
+ currentListeners .onFailure (new NotMasterException ("delayed reroute [" + reason + "] cancelled" ));
123
+ // no big deal, the new master will reroute again
101
124
}
102
125
103
126
@ Override
104
127
public void onFailure (String source , Exception e ) {
105
- rerouting .set (false );
106
- ClusterState state = clusterService .state ();
128
+ synchronized (mutex ) {
129
+ if (pendingRerouteListeners == currentListeners ) {
130
+ pendingRerouteListeners = null ;
131
+ }
132
+ }
133
+ final ClusterState state = clusterService .state ();
107
134
if (logger .isTraceEnabled ()) {
108
135
logger .error (() -> new ParameterizedMessage ("unexpected failure during [{}], current state:\n {}" ,
109
136
source , state ), e );
110
137
} else {
111
138
logger .error (() -> new ParameterizedMessage ("unexpected failure during [{}], current state version [{}]" ,
112
139
source , state .version ()), e );
113
140
}
141
+ currentListeners .onFailure (new ElasticsearchException ("delayed reroute [" + reason + "] failed" , e ));
142
+ }
143
+
144
+ @ Override
145
+ public void clusterStateProcessed (String source , ClusterState oldState , ClusterState newState ) {
146
+ currentListeners .onResponse (null );
114
147
}
115
148
});
116
149
} catch (Exception e ) {
117
- rerouting .set (false );
150
+ synchronized (mutex ) {
151
+ assert pendingRerouteListeners == currentListeners ;
152
+ pendingRerouteListeners = null ;
153
+ }
118
154
ClusterState state = clusterService .state ();
119
155
logger .warn (() -> new ParameterizedMessage ("failed to reroute routing table, current state:\n {}" , state ), e );
156
+ currentListeners .onFailure (new ElasticsearchException ("delayed reroute [" + reason + "] could not be submitted" , e ));
120
157
}
121
158
}
122
159
}
0 commit comments