in brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java [265:457]
public void changeMode(HighAvailabilityMode startMode, boolean preventElectionOnExplicitStandbyMode, boolean failOnExplicitModesIfUnusual) {
if (!running) {
// if was not running then start as disabled mode, then proceed as normal
LOG.info("HA changing mode to "+startMode+" from "+getInternalNodeState()+" when not running, forcing an intermediate start as DISABLED then will convert to "+startMode);
start(HighAvailabilityMode.DISABLED);
}
if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.INITIALIZING) {
if (startMode!=HighAvailabilityMode.DISABLED) {
// if coming from FAILED (or INITIALIZING because we skipped start call) then treat as initializing
setInternalNodeState(ManagementNodeState.INITIALIZING);
}
}
ownNodeId = managementContext.getManagementNodeId();
// TODO Small race in that we first check, and then we'll do checkMaster() on first poll,
// so another node could have already become master or terminated in that window.
ManagementNodeSyncRecord existingMaster = hasHealthyMaster();
boolean weAreRecognisedAsMaster = existingMaster!=null && ownNodeId.equals(existingMaster.getNodeId());
boolean weAreMasterLocally = getInternalNodeState()==ManagementNodeState.MASTER;
// catch error in some tests where mgmt context has a different HA manager
if (managementContext.getHighAvailabilityManager()!=this)
throw new IllegalStateException("Cannot start an HA manager on a management context with a different HA manager!");
if (weAreMasterLocally) {
// demotion may be required; do this before triggering an election
switch (startMode) {
case MASTER:
case AUTO:
case DISABLED:
// no action needed, will do anything necessary below (or above)
break;
case HOT_STANDBY:
case HOT_BACKUP:
case STANDBY:
demoteTo(ManagementNodeState.of(startMode).get()); break;
default:
throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
}
}
ManagementNodeState oldState = getInternalNodeState();
// now do election
switch (startMode) {
case AUTO:
// don't care; let's start and see if we promote ourselves
if (getInternalNodeState()==ManagementNodeState.INITIALIZING) {
setInternalNodeState(ManagementNodeState.STANDBY);
}
publishAndCheck(true);
switch (getInternalNodeState()) {
case HOT_BACKUP:
if (!nodeStateTransitionComplete) throw new IllegalStateException("Cannot switch to AUTO when in the middle of a transition to "+getInternalNodeState());
// else change us to standby, desiring to go to hot standby, and continue to below
setInternalNodeState(ManagementNodeState.STANDBY);
startMode = HighAvailabilityMode.HOT_BACKUP;
case HOT_STANDBY:
case STANDBY:
if (getInternalNodeState()==ManagementNodeState.STANDBY && oldState==ManagementNodeState.INITIALIZING && startMode!=HighAvailabilityMode.HOT_BACKUP
&& BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY)) {
// auto requested; not promoted; so it should become hot standby
startMode = HighAvailabilityMode.HOT_STANDBY;
}
ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
String masterNodeId = newState.getMasterNodeId();
ManagementNodeSyncRecord masterNodeDetails = newState.getManagementNodes().get(masterNodeId);
LOG.info("Management node "+ownNodeId+" running as HA " + getInternalNodeState() + " autodetected"
+ (startMode == HighAvailabilityMode.HOT_STANDBY || startMode == HighAvailabilityMode.HOT_BACKUP ?
" (will change to "+startMode+")" : "")
+ ", " +
(Strings.isBlank(masterNodeId) ? "no master currently (other node should promote itself soon)" : "master "
+ (existingMaster==null ? "(new) " : "")
+ "is "+masterNodeId +
(masterNodeDetails==null || masterNodeDetails.getUri()==null ? " (no url)" : " at "+masterNodeDetails.getUri())));
break;
case MASTER:
LOG.info("Management node "+ownNodeId+" running as HA MASTER autodetected");
break;
default:
throw new IllegalStateException("Management node "+ownNodeId+" set to HA AUTO, encountered unexpected mode "+getInternalNodeState());
}
break;
case MASTER:
if (!failOnExplicitModesIfUnusual || existingMaster==null) {
promoteToMaster();
if (existingMaster!=null) {
LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly");
} else {
LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly, stealing from "+existingMaster);
}
} else if (!weAreRecognisedAsMaster) {
throw new IllegalStateException("Master already exists; cannot run as master (master "+existingMaster.toVerboseString()+"); "
+ "to trigger a promotion, set a priority and demote the current master");
} else {
LOG.info("Management node "+ownNodeId+" already running as HA MASTER, when set explicitly");
}
break;
case HOT_BACKUP:
setInternalNodeState(ManagementNodeState.HOT_BACKUP);
// then continue into next block
case STANDBY:
case HOT_STANDBY:
if (startMode!=HighAvailabilityMode.HOT_BACKUP) {
if (ManagementNodeState.isHotProxy(getInternalNodeState()) && startMode==HighAvailabilityMode.HOT_STANDBY) {
// if was hot_backup, we can immediately go hot_standby
setInternalNodeState(ManagementNodeState.HOT_STANDBY);
} else {
// from any other state, set standby, then perhaps switch to hot_standby later on (or might become master in the next block)
setInternalNodeState(ManagementNodeState.STANDBY);
}
}
if (ManagementNodeState.isStandby(getInternalNodeState())) {
if (!preventElectionOnExplicitStandbyMode) {
publishAndCheck(true);
}
if (failOnExplicitModesIfUnusual && existingMaster==null) {
LOG.error("Management node "+ownNodeId+" detected no master when "+startMode+" requested and existing master required; failing.");
throw new IllegalStateException("No existing master; cannot start as "+startMode);
}
}
String message = "Management node "+ownNodeId+" running as HA "+getNodeState()+" (";
if (getNodeState().toString().equals(startMode.toString()))
message += "explicitly requested";
else if (startMode==HighAvailabilityMode.HOT_STANDBY && getNodeState()==ManagementNodeState.STANDBY)
message += "caller requested "+startMode+", will attempt rebind for HOT_STANDBY next";
else
message += "caller requested "+startMode;
if (getNodeState()==ManagementNodeState.MASTER) {
message += " but election re-promoted this node)";
} else {
ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
if (Strings.isBlank(newState.getMasterNodeId())) {
message += "); no master currently";
if (startMode != HighAvailabilityMode.HOT_BACKUP) message += " (subsequent election may repair)";
} else {
message += "); master "+newState.getMasterNodeId();
}
}
LOG.info(message);
break;
case DISABLED:
// safe just to run even if we weren't master
LOG.info("Management node "+ownNodeId+" HA DISABLED (was "+getInternalNodeState()+")");
demoteTo(ManagementNodeState.FAILED);
if (pollingTask!=null) pollingTask.cancel(true);
break;
default:
throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
}
if ((startMode==HighAvailabilityMode.HOT_STANDBY || startMode==HighAvailabilityMode.HOT_BACKUP)) {
if (!ManagementNodeState.isHotProxy(oldState)) {
// now transition to hot proxy
nodeStateTransitionComplete = false;
if (startMode==HighAvailabilityMode.HOT_STANDBY) {
// if it should be hot standby, then we may need to promote
// inform the world that we are transitioning (but not eligible for promotion while going in to hot standby)
// (no harm in doing this twice)
publishHealth();
}
try {
activateHotProxy(ManagementNodeState.of(startMode).get()).get();
// error above now throws
nodeStateTransitionComplete = true;
publishHealth();
if (getNodeState()==ManagementNodeState.HOT_STANDBY || getNodeState()==ManagementNodeState.HOT_BACKUP) {
LOG.info("Management node "+ownNodeId+" now running as HA "+getNodeState()+"; "
+ managementContext.getApplications().size()+" application"+Strings.s(managementContext.getApplications().size())+" loaded");
} else {
// shouldn't come here, we should have gotten an error above
LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); "
+ "(see log for further details)");
}
} catch (Exception e) {
LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); rethrowing: "+Exceptions.collapseText(e));
nodeStateTransitionComplete = true;
throw Exceptions.propagate(e);
}
} else {
// transitioning among hot proxy states - tell the rebind manager
managementContext.getRebindManager().stopReadOnly();
managementContext.getRebindManager().startReadOnly(ManagementNodeState.of(startMode).get());
nodeStateTransitionComplete = true;
}
} else {
nodeStateTransitionComplete = true;
}
if (startMode!=HighAvailabilityMode.DISABLED)
registerPollTask();
}