16
16
package org .terracotta .testing .master ;
17
17
18
18
import java .io .IOException ;
19
+ import java .util .EmptyStackException ;
19
20
import java .util .List ;
21
+ import java .util .Stack ;
20
22
import java .util .Vector ;
21
23
22
24
import org .terracotta .testing .common .Assert ;
@@ -34,6 +36,7 @@ public class SynchronousProcessControl implements IMultiProcessControl {
34
36
private final List <ServerProcess > unknownServers = new Vector <ServerProcess >();
35
37
// It is invalid to use a process control object which has already been shut down so keep that flag.
36
38
private boolean isShutDown ;
39
+ private Stack <ServerProcess > terminatedServers = new Stack ();
37
40
38
41
public SynchronousProcessControl (ITestStateManager stateManager , ContextualLogger logger ) {
39
42
this .stateManager = stateManager ;
@@ -84,6 +87,60 @@ public synchronized void restartActive() {
84
87
this .logger .output ("<<< restartActive" );
85
88
}
86
89
90
+ @ Override
91
+ public void terminateActive () {
92
+ this .logger .output (">>> terminateActive" );
93
+ verifyNotShutdown ();
94
+ // First, make sure that there is an active.
95
+ internalWaitForActive ();
96
+ // We MUST now have an active.
97
+ Assert .assertTrue (null != this .activeServer );
98
+
99
+ // Remove the active.
100
+ ServerProcess victim = this .activeServer ;
101
+ this .activeServer = null ;
102
+ // Stop it.
103
+ try {
104
+ int ret = victim .stop ();
105
+ this .logger .output ("terminated server, return status: " + ret );
106
+ } catch (InterruptedException e ) {
107
+ // We can't leave a consistent state if interrupted at this point.
108
+ Assert .unexpected (e );
109
+ }
110
+ // Return the process to its installation and get a new one.
111
+ ServerInstallation underlyingInstallation = victim .getUnderlyingInstallation ();
112
+ underlyingInstallation .retireProcess (victim );
113
+
114
+ // Close its logs.
115
+ try {
116
+ underlyingInstallation .closeStandardLogFiles ();
117
+ } catch (IOException e ) {
118
+ // We don't expect this IOException on closing the logs.
119
+ Assert .unexpected (e );
120
+ }
121
+ }
122
+
123
+ @ Override
124
+ public void startLastTerminatedServer () {
125
+ try {
126
+ ServerProcess lastTerminatedServer = terminatedServers .pop ();
127
+
128
+ ServerInstallation underlyingInstallation = lastTerminatedServer .getUnderlyingInstallation ();
129
+ ServerProcess freshProcess = underlyingInstallation .createNewProcess (this .stateManager );
130
+
131
+ // Start it.
132
+ long pid = freshProcess .start ();
133
+ this .logger .output ("Server restarted with PID: " + pid );
134
+ // Enqueue it onto the unknown list.
135
+ this .unknownServers .add (freshProcess );
136
+
137
+ // At this point, we don't know the active server.
138
+ this .logger .output ("<<< restartActive" );
139
+ } catch (EmptyStackException e ) {
140
+ throw new RuntimeException ("There are no terminated processes" );
141
+ }
142
+ }
143
+
87
144
@ Override
88
145
public synchronized void shutDown () {
89
146
this .logger .output (">>> shutDown" );
@@ -175,14 +232,23 @@ private void internalWaitForActive() {
175
232
waitForAllUnknowns ();
176
233
// If we still have no active, walk the passives to see if one was promoted.
177
234
if (null == this .activeServer ) {
178
- // Note that we don't want to walk this list while placing servers in states since looping the list, at this point, is either a bug or a serious race condition.
179
- Vector <ServerProcess > oldPassives = new Vector <ServerProcess >(this .passiveServers );
180
- this .passiveServers .clear ();
181
- while (!oldPassives .isEmpty ()) {
182
- ServerProcess passive = oldPassives .remove (0 );
183
- waitAndPlaceServerInState (passive );
235
+ checkAllPassivesState ();
236
+
237
+ // If we still can't find an active, do multiple retries with some timeout as it might take sometime for
238
+ // other passives to become active (Note that this is a hacky way to fix the issue for now, we are going to
239
+ // address this properly later)
240
+ if (null == this .activeServer ) {
241
+ int retryCount = 30 ;
242
+ while (retryCount -- != 0 ) {
243
+ try {
244
+ Thread .sleep (1000 );
245
+ checkAllPassivesState ();
246
+ } catch (InterruptedException e ) {
247
+ Assert .unexpected (e );
248
+ }
249
+ }
184
250
}
185
-
251
+
186
252
// See if we are still without an active.
187
253
if (null == this .activeServer ) {
188
254
// If there is no active at this point, it probably means that there is something seriously wrong with the
@@ -193,6 +259,16 @@ private void internalWaitForActive() {
193
259
}
194
260
}
195
261
262
+ private void checkAllPassivesState () {
263
+ // Note that we don't want to walk this list while placing servers in states since looping the list, at this point, is either a bug or a serious race condition.
264
+ Vector <ServerProcess > oldPassives = new Vector <ServerProcess >(this .passiveServers );
265
+ this .passiveServers .clear ();
266
+ while (!oldPassives .isEmpty ()) {
267
+ ServerProcess passive = oldPassives .remove (0 );
268
+ waitAndPlaceServerInState (passive );
269
+ }
270
+ }
271
+
196
272
private void waitForAllUnknowns () {
197
273
while (!this .unknownServers .isEmpty ()) {
198
274
ServerProcess unknown = this .unknownServers .remove (0 );
0 commit comments