final int localSyncN = participantsStates.getLocalSyncN();
List<ConditionClient> slaves = participantsStates.getConditionClients();
if (localSyncN > 0) {
// always get the latest available state of all servers
LSN latest = null;
Map<ClientInterface ,LSN> states = getStates(slaves, true);
// getting enough slaves has failed
if (states.size() < localSyncN) {
throw new BabuDBException(ErrorCode.REPLICATION_FAILURE,
"Not enough slaves available to synchronize with!");
} else {
List<LSN> values = new LinkedList<LSN>(states.values());
Collections.sort(values, Collections.reverseOrder());
latest = values.get(0);
}
// synchronize with the most up-to-date slave, if necessary
LSN localState = babuDB.getState();
if (localState.compareTo(latest) < 0) {
for (Entry<ClientInterface, LSN> entry : states.entrySet()) {
if (entry.getValue().equals(latest)) {
Logging.logMessage(Logging.LEVEL_INFO, this,
"Starting synchronization from '%s' to '%s'.",
localState.toString(), latest.toString());
BabuDBRequestResultImpl<Object> ready = babuDB.createRequestFuture();
replicationStage.manualLoad(ready, latest);
ready.get();
assert(latest.equals(babuDB.getState())) :
"Synchronization failed: (expected=" +
latest.toString() + ") != (acknowledged=" +
babuDB.getState() + ")";
break;
}
}
}
}
// take a checkpoint on master-failover (inclusive viewId incrementation), if necessary
LSN beforeCP = lastOnView.get();
if (babuDB.getState().getSequenceNo() > 0L) {
Logging.logMessage(Logging.LEVEL_DEBUG, this, "taking a checkpoint");
beforeCP = babuDB.checkpoint();
lastOnView.set(beforeCP);
}
Logging.logMessage(Logging.LEVEL_INFO, this,
"Agreed to synchronize to %s before the next view.", beforeCP.toString());
// wait for the slaves to recognize the master-change and for at least N servers to
// establish a stable state
LSN syncState = babuDB.getState();
final ReplicateResponse result = new ReplicateResponse(syncState, listener,
slaves.size() - localSyncN);
subscribeListener(result);
for (ConditionClient slave : slaves) {
slave.synchronize(beforeCP, port).registerListener(
new ClientResponseAvailableListener<Object>() {
@Override
public void responseAvailable(Object r) { /* ignored */ }
@Override
public void requestFailed(Exception e) {
result.decrementPermittedFailures();
}
});
}
Logging.logMessage(Logging.LEVEL_INFO, this,
"The next view will start with %s.", syncState.toString());
}