More robust stop signal configuration

This commit is contained in:
Electroid 2017-07-28 13:26:27 -07:00
parent 0caea52598
commit 99ae043580
4 changed files with 44 additions and 25 deletions

View File

@ -1,7 +1,5 @@
package tc.oc.commons.core.restart;
import javax.annotation.Nullable;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
@ -41,23 +39,17 @@ public class RequestRestartEvent {
}
public Deferral defer(String deferrerName) {
return defer(deferrerName, null);
}
public Deferral defer(String deferrerName, @Nullable Duration predictedDelay) {
logger.info("Restart deferred by " + deferrerName);
Deferral deferral = new Deferral(deferrerName, predictedDelay);
Deferral deferral = new Deferral(deferrerName);
deferrals.add(deferral);
return deferral;
}
public class Deferral {
private final String deferrerName;
private final Duration predictedDelay;
public Deferral(String deferrerName, @Nullable Duration predictedDelay) {
public Deferral(String deferrerName) {
this.deferrerName = deferrerName;
this.predictedDelay = predictedDelay;
}
public RequestRestartEvent request() {
@ -68,10 +60,6 @@ public class RequestRestartEvent {
return deferrerName;
}
public Duration predictedDelay() {
return predictedDelay;
}
/**
* Allow the deferred restart to proceed. After this method is called,
* this object becomes useless and can be discarded.

View File

@ -63,6 +63,20 @@ public class RestartConfiguration {
* Restart the server when any of the given stop signals are received from the system.
*/
public Set<String> stopSignals() {
return ConfigUtils.getStringSet(config, "stop-signals", Sets.newHashSet("INT", "TERM"));
return ConfigUtils.getStringSet(config, "stop-signal.triggers", Sets.newHashSet("INT", "TERM"));
}
/**
* The priority that stop signals will restart the server with.
*/
public Integer stopSignalPriority() {
return config.getInt("stop-signal.priority", Integer.MAX_VALUE);
}
/**
* Maximum time the server will wait for deferals to resume before forcing a restart.
*/
public Duration stopSignalTimeout() {
return ConfigUtils.getDuration(config, "stop-signal.timeout", Duration.ofHours(6));
}
}

View File

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -62,7 +63,6 @@ public class RestartManager implements PluginFacet, Tickable {
this.eventBus = eventBus;
this.threads = threads;
this.startTime = Instant.now();
onSignal(this.config.stopSignals());
}
@Override
@ -85,6 +85,8 @@ public class RestartManager implements PluginFacet, Tickable {
TimeUnit.MILLISECONDS
);
}
// Enable listeners for stop signals
onSignal(this.config.stopSignals());
}
@Override
@ -248,20 +250,35 @@ public class RestartManager implements PluginFacet, Tickable {
signals.stream()
.map(Signal::new)
.forEach(signal -> Signal.handle(signal, s -> {
requestRestartInternal(Instant.now(), "Received signal " + s.getName() + " (" + s.getNumber() + ") from system", Integer.MAX_VALUE);
String reason = "Received signal " + s.getName() + " (" + s.getNumber() + ") from system";
int priority = config.stopSignalPriority();
try {
Thread.sleep(currentRequest.deferrals()
.stream()
.filter(deferral -> deferral.predictedDelay() != null)
.map(RequestRestartEvent.Deferral::predictedDelay)
.findFirst()
.orElse(Duration.ZERO)
.toMillis() + 1L);
// Attempt to send restart request to the api
requestRestart(reason, priority).get();
// Wait for restart to sync back to the server
boolean success = true;
Instant start = Instant.now();
while(currentRequest == null || !currentRequest.reason().equals(reason)) {
Thread.sleep(Duration.ofSeconds(1).toMillis());
if(Duration.between(start, Instant.now()).getSeconds() > 5) {
logger.warning("Unable to request a " + signal.getName() + " signal restart to the api");
success = false;
}
}
if(!success) throw new InterruptedException();
} catch(InterruptedException | ExecutionException e) {
// Fallback to sending the request via local server events
requestRestartInternal(Instant.now(), reason, priority);
}
try {
// Sleep until maximum timeout before forcibly stopping the server
Thread.sleep(config.stopSignalTimeout().toMillis());
} catch(InterruptedException e) {
if(!minecraftServer.isStopping()) {
logger.severe(s.getName() + " signal is unable to wait for restart");
}
}
// Stop the server is all other restart attempts have failed
if(!restartIfRequested() && !minecraftServer.isStopping()) {
minecraftServer.stop();
}

View File

@ -142,7 +142,7 @@ public class RestartListener implements PluginFacet, Listener {
public void onRequestRestart(RequestRestartEvent event) {
if(!server.isSuspended()) {
logger.info("Deferring restart");
deferral = event.defer(getClass().getName(), config.time());
deferral = event.defer(getClass().getName());
checkCountdown(mm.needCurrentMatch());
}
}