package com.webobjects.monitor.application.starter; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableArray; import com.webobjects.monitor._private.MApplication; import com.webobjects.monitor._private.MHost; import com.webobjects.monitor._private.MInstance; /** * Bounces an application using a rolling shutdown. * * It does so by starting at least one inactive instance per active host * (or 10 % of the total active instance count), waiting until they have started, * then forcefully restarting each instance one at a time until they have all * been restarted. * * You must have at least one inactive instance in order to perform this bounce. * * @author johnthuss */ public class RollingShutdownBouncer extends ApplicationStarter { public RollingShutdownBouncer(MApplication app) { super(app); } @Override protected void bounce() throws InterruptedException { NSArray<MInstance> instances = application().instanceArray().immutableClone(); NSArray<MInstance> runningInstances = application().runningInstances_M(); NSArray<MHost> activeHosts = (NSArray<MHost>) runningInstances.valueForKeyPath("host.@unique"); NSMutableArray<MInstance> inactiveInstances = instances.mutableClone(); inactiveInstances.removeObjectsInArray(runningInstances); if (inactiveInstances.isEmpty()) { addObjectsFromArrayIfAbsentToErrorMessageArray( new NSArray<>("You must have at least one inactive instance to perform a rolling shutdown bounce.")); return; } int numInstancesToStartPerHost = numInstancesToStartPerHost(runningInstances, activeHosts); NSArray<MInstance> startingInstances = instancesToStart(inactiveInstances, activeHosts, numInstancesToStartPerHost); boolean useScheduling = doAllRunningInstancesUseScheduling(runningInstances); log("Starting inactive instances"); startInstances(startingInstances, activeHosts, useScheduling); waitForInactiveInstancesToStart(startingInstances, activeHosts); NSMutableArray<MInstance> restartingInstances = runningInstances.mutableClone(); refuseNewSessions(restartingInstances, activeHosts); NSMutableArray<MInstance> stoppingInstances = new NSMutableArray<>(); for (int i = numInstancesToStartPerHost; i > 0; i--) { if (restartingInstances.isEmpty()) { break; } stoppingInstances.addObject(restartingInstances.removeLastObject()); } restartInstances(restartingInstances, activeHosts, useScheduling); stopInstances(stoppingInstances, activeHosts); handler().startReading(); try { handler().getInstanceStatusForHosts(activeHosts); log("Finished"); } finally { handler().endReading(); } } protected int numInstancesToStartPerHost(NSArray<MInstance> runningInstances, NSArray<MHost> activeHosts) { int numToStartPerHost = 1; if (activeHosts.count() > 0) { numToStartPerHost = (int) (runningInstances.count() / activeHosts.count() * .1); } if (numToStartPerHost < 1) { numToStartPerHost = 1; } return numToStartPerHost; } protected NSArray<MInstance> instancesToStart(NSArray<MInstance> inactiveInstances, NSArray<MHost> activeHosts, int numInstancesToStartPerHost) { NSMutableArray<MInstance> startingInstances = new NSMutableArray<>(); for (int i = 0; i < numInstancesToStartPerHost; i++) { for (MHost host : activeHosts) { NSArray<MInstance> inactiveInstancesForHost = MInstance.HOST.eq(host).filtered(inactiveInstances); if (inactiveInstancesForHost != null && inactiveInstancesForHost.count() >= i) { MInstance instance = inactiveInstancesForHost.objectAtIndex(i); log("Starting inactive instance " + instance.displayName() + " on host " + host.addressAsString()); startingInstances.addObject(instance); } else { log("Not enough inactive instances on host: " + host.addressAsString()); } } } return startingInstances.immutableClone(); } protected boolean doAllRunningInstancesUseScheduling(NSArray<MInstance> runningInstances) { boolean useScheduling = true; for (MInstance instance : runningInstances) { useScheduling &= instance.schedulingEnabled() != null && instance.schedulingEnabled().booleanValue(); } return useScheduling; } protected void startInstances(NSArray<MInstance> startingInstances, NSArray<MHost> activeHosts, boolean useScheduling) { for (MInstance instance : startingInstances) { if (useScheduling) { instance.setSchedulingEnabled(Boolean.TRUE); } instance.setAutoRecover(Boolean.TRUE); } handler().sendUpdateInstancesToWotaskds(startingInstances, activeHosts); handler().sendStartInstancesToWotaskds(startingInstances, activeHosts); } protected void waitForInactiveInstancesToStart(NSArray<MInstance> startingInstances, NSArray<MHost> activeHosts) throws InterruptedException { boolean waiting = true; // wait until apps have started while (waiting) { handler().startReading(); try { log("Checking to see if inactive instances have started"); handler().getInstanceStatusForHosts(activeHosts); boolean allStarted = true; for (MInstance instance : startingInstances) { allStarted &= instance.isRunning_M(); } if (allStarted) { waiting = false; } else { sleep(10 * 1000); } } finally { handler().endReading(); } } log("Started inactive instances sucessfully"); } protected void refuseNewSessions(NSArray<MInstance> restartingInstances, NSArray<MHost> activeHosts) { for (MInstance instance : restartingInstances) { instance.setRefusingNewSessions(true); } handler().sendRefuseSessionToWotaskds(restartingInstances, activeHosts, true); } protected void restartInstances(NSArray<MInstance> runningInstances, NSArray<MHost> activeHosts, boolean useScheduling) throws InterruptedException { for (MInstance instance : runningInstances) { NSArray<MInstance> instanceInArray = new NSArray<>(instance); handler().sendStopInstancesToWotaskds(instanceInArray, activeHosts); sleep(10 * 1000); handler().sendUpdateInstancesToWotaskds(instanceInArray, activeHosts); startInstances(instanceInArray, activeHosts, useScheduling); waitForInactiveInstancesToStart(instanceInArray, activeHosts); log("Restarted instance " + instance.displayName() + " sucessfully"); } } protected void stopInstances(NSMutableArray<MInstance> stoppingInstances, NSArray<MHost> activeHosts) { for (MInstance instance : stoppingInstances) { instance.setSchedulingEnabled(Boolean.FALSE); instance.setAutoRecover(Boolean.FALSE); } handler().sendUpdateInstancesToWotaskds(stoppingInstances, activeHosts); handler().sendStopInstancesToWotaskds(stoppingInstances, activeHosts); log("Stopped instances " + stoppingInstances.toString() + " sucessfully"); } }