24
24
import org .elasticsearch .ExceptionsHelper ;
25
25
import org .elasticsearch .action .ActionListener ;
26
26
import org .elasticsearch .action .support .ChannelActionListener ;
27
+ import org .elasticsearch .action .support .PlainActionFuture ;
27
28
import org .elasticsearch .cluster .routing .ShardRouting ;
28
29
import org .elasticsearch .common .Nullable ;
30
+ import org .elasticsearch .common .component .AbstractLifecycleComponent ;
29
31
import org .elasticsearch .common .inject .Inject ;
30
32
import org .elasticsearch .common .settings .Settings ;
33
+ import org .elasticsearch .common .util .concurrent .FutureUtils ;
31
34
import org .elasticsearch .index .IndexService ;
32
35
import org .elasticsearch .index .shard .IndexEventListener ;
33
36
import org .elasticsearch .index .shard .IndexShard ;
50
53
* The source recovery accepts recovery requests from other peer shards and start the recovery process from this
51
54
* source shard to the target shard.
52
55
*/
53
- public class PeerRecoverySourceService implements IndexEventListener {
56
+ public class PeerRecoverySourceService extends AbstractLifecycleComponent implements IndexEventListener {
54
57
55
58
private static final Logger logger = LogManager .getLogger (PeerRecoverySourceService .class );
56
59
@@ -74,6 +77,19 @@ public PeerRecoverySourceService(TransportService transportService, IndicesServi
74
77
new StartRecoveryTransportRequestHandler ());
75
78
}
76
79
80
+ @ Override
81
+ protected void doStart () {
82
+ }
83
+
84
+ @ Override
85
+ protected void doStop () {
86
+ ongoingRecoveries .awaitEmpty ();
87
+ }
88
+
89
+ @ Override
90
+ protected void doClose () {
91
+ }
92
+
77
93
@ Override
78
94
public void beforeIndexShardClosed (ShardId shardId , @ Nullable IndexShard indexShard ,
79
95
Settings indexSettings ) {
@@ -118,9 +134,14 @@ final int numberOfOngoingRecoveries() {
118
134
}
119
135
120
136
final class OngoingRecoveries {
137
+
121
138
private final Map <IndexShard , ShardRecoveryContext > ongoingRecoveries = new HashMap <>();
122
139
140
+ @ Nullable
141
+ private List <ActionListener <Void >> emptyListeners ;
142
+
123
143
synchronized RecoverySourceHandler addNewRecovery (StartRecoveryRequest request , IndexShard shard ) {
144
+ assert lifecycle .started ();
124
145
final ShardRecoveryContext shardContext = ongoingRecoveries .computeIfAbsent (shard , s -> new ShardRecoveryContext ());
125
146
RecoverySourceHandler handler = shardContext .addNewRecovery (request , shard );
126
147
shard .recoveryStats ().incCurrentAsSource ();
@@ -138,6 +159,13 @@ synchronized void remove(IndexShard shard, RecoverySourceHandler handler) {
138
159
if (shardRecoveryContext .recoveryHandlers .isEmpty ()) {
139
160
ongoingRecoveries .remove (shard );
140
161
}
162
+ if (ongoingRecoveries .isEmpty ()) {
163
+ if (emptyListeners != null ) {
164
+ final List <ActionListener <Void >> onEmptyListeners = emptyListeners ;
165
+ emptyListeners = null ;
166
+ ActionListener .onResponse (onEmptyListeners , null );
167
+ }
168
+ }
141
169
}
142
170
143
171
synchronized void cancel (IndexShard shard , String reason ) {
@@ -157,6 +185,22 @@ synchronized void cancel(IndexShard shard, String reason) {
157
185
}
158
186
}
159
187
188
+ void awaitEmpty () {
189
+ assert lifecycle .stoppedOrClosed ();
190
+ final PlainActionFuture <Void > future ;
191
+ synchronized (this ) {
192
+ if (ongoingRecoveries .isEmpty ()) {
193
+ return ;
194
+ }
195
+ future = new PlainActionFuture <>();
196
+ if (emptyListeners == null ) {
197
+ emptyListeners = new ArrayList <>();
198
+ }
199
+ emptyListeners .add (future );
200
+ }
201
+ FutureUtils .get (future );
202
+ }
203
+
160
204
private final class ShardRecoveryContext {
161
205
final Set <RecoverySourceHandler > recoveryHandlers = new HashSet <>();
162
206
0 commit comments