package org.corfudb.infrastructure;
import lombok.extern.slf4j.Slf4j;
import org.corfudb.runtime.exceptions.LayoutModificationException;
import org.corfudb.runtime.view.Layout;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Allows us to make modifications to a layout.
* <p>
* Created by zlokhandwala on 10/12/16.
*/
@Slf4j
public class LayoutWorkflowManager {
/**
* Updated layout
*/
private Layout layout;
/**
* Stores the epoch of the layout.
*/
private long epoch;
/**
* Copies the attributes of the layout to make modifications.
*
* @param layout Base layout to be modified.
*/
public LayoutWorkflowManager(Layout layout) throws CloneNotSupportedException {
this.layout = (Layout) layout.clone();
this.epoch = layout.getEpoch();
}
/**
* Adds unresponsive servers in the list.
*
* @param endpoints Endpoints to be added.
* @return
*/
public LayoutWorkflowManager addUnresponsiveServers(Set<String> endpoints) {
List<String> unresponsiveServers = layout.getUnresponsiveServers();
unresponsiveServers.clear();
endpoints.forEach(unresponsiveServers::add);
return this;
}
/**
* Removes the Layout Server passed
*
* @param endpoint Endpoint to be removed
* @return Workflow manager
* @throws LayoutModificationException If attempt to remove all layout servers.
*/
public LayoutWorkflowManager removeLayoutServer(String endpoint)
throws LayoutModificationException {
List<String> layoutServers = layout.getLayoutServers();
for (int i = 0; i < layoutServers.size(); i++) {
if (layoutServers.get(i).equals(endpoint)) {
if (layoutServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all layout servers.");
}
layoutServers.remove(i);
return this;
}
}
return this;
}
/**
* Removes all the endpoints present in the set passed.
*
* @param endpoints Layout server endpoints to be removed from the layout.
* @return Workflow manager
* @throws LayoutModificationException If attempt to remove all layout servers.
*/
public LayoutWorkflowManager removeLayoutServers(Set<String> endpoints)
throws LayoutModificationException {
// Not making changes in the original list in case of exceptions.
// Copy the list so that we can have an atomic result and no partial removals
List<String> modifiedLayoutServers = new ArrayList<>(layout.getLayoutServers());
for (int i = 0; i < modifiedLayoutServers.size(); ) {
if (endpoints.contains(modifiedLayoutServers.get(i))) {
if (modifiedLayoutServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all layout servers.");
}
modifiedLayoutServers.remove(i);
} else {
i++;
}
}
layout.getLayoutServers().retainAll(modifiedLayoutServers);
return this;
}
/**
* Moves a responsive server to the top of the sequencer server list.
* If all have failed, throws exception.
*
* @param endpoints Failed endpoints.
* @return LayoutWorkflowManager
* @throws LayoutModificationException throws if no working sequencer left.
*/
public LayoutWorkflowManager moveResponsiveSequencerToTop(Set<String> endpoints)
throws LayoutModificationException {
List<String> modifiedSequencerServers = new ArrayList<>(layout.getSequencers());
for (int i = 0; i < modifiedSequencerServers.size(); i++) {
String sequencerServer = modifiedSequencerServers.get(i);
if (!endpoints.contains(sequencerServer)) {
modifiedSequencerServers.remove(sequencerServer);
modifiedSequencerServers.add(0, sequencerServer);
layout.setSequencers(modifiedSequencerServers);
return this;
}
}
throw new LayoutModificationException("All sequencers failed.");
}
/**
* Removes sequencer endpoint from the layout
*
* @param endpoint Sequencer server to be removed
* @return Workflow manager
* @throws LayoutModificationException Cannot remove the only sequencer server.
*/
public LayoutWorkflowManager removeSequencerServer(String endpoint)
throws LayoutModificationException {
List<String> sequencerServers = layout.getSequencers();
for (int i = 0; i < sequencerServers.size(); i++) {
if (sequencerServers.get(i).equals(endpoint)) {
if (sequencerServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all sequencers.");
}
sequencerServers.remove(i);
return this;
}
}
return this;
}
/**
* Removes sequencer endpoints from the layout
*
* @param endpoints Set of sequencer servers to be removed
* @return Workflow manager
* @throws LayoutModificationException Cannot remove the only sequencer server.
*/
public LayoutWorkflowManager removeSequencerServers(Set<String> endpoints)
throws LayoutModificationException {
// Not making changes in the original list in case of exceptions.
// Copy the list so that we can have an atomic result and no partial removals
List<String> modifiedSequencerServers = new ArrayList<>(layout.getSequencers());
for (int i = 0; i < modifiedSequencerServers.size(); ) {
String sequencerServer = modifiedSequencerServers.get(i);
if (endpoints.contains(sequencerServer)) {
if (modifiedSequencerServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all sequencers.");
}
modifiedSequencerServers.remove(i);
} else {
i++;
}
}
layout.getSequencers().retainAll(modifiedSequencerServers);
return this;
}
/**
* Removes the logunit endpoint from the layout
*
* @param endpoint Log unit server to be removed
* @return Workflow manager
* @throws LayoutModificationException Cannot remove non-replicated logunit
*/
public LayoutWorkflowManager removeLogunitServer(String endpoint)
throws LayoutModificationException {
List<Layout.LayoutSegment> layoutSegments = layout.getSegments();
for (Layout.LayoutSegment layoutSegment : layoutSegments) {
for (Layout.LayoutStripe layoutStripe : layoutSegment.getStripes()) {
List<String> loguintServers = layoutStripe.getLogServers();
for (int k = 0; k < loguintServers.size(); k++) {
if (loguintServers.get(k).equals(endpoint)) {
if (loguintServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all logunit. " +
"No replicas available.");
}
loguintServers.remove(k);
return this;
}
}
}
}
return this;
}
/**
* Removes the logunit endpoints from the layout
*
* @param endpoints Log unit servers to be removed
* @return Workflow manager
* @throws LayoutModificationException Cannot remove non-replicated logunit
*/
public LayoutWorkflowManager removeLogunitServers(Set<String> endpoints)
throws LayoutModificationException {
// Not making changes in the original list in case of exceptions.
// Copy the list so that we can have an atomic result and no partial removals
List<Layout.LayoutSegment> modifiedLayoutSegments = new ArrayList<>(layout.getSegments());
for (Layout.LayoutSegment layoutSegment : modifiedLayoutSegments) {
for (Layout.LayoutStripe layoutStripe : layoutSegment.getStripes()) {
List<String> loguintServers = layoutStripe.getLogServers();
for (int k = 0; k < loguintServers.size(); ) {
String logunitServer = loguintServers.get(k);
if (endpoints.contains(logunitServer)) {
if (loguintServers.size() == 1) {
throw new LayoutModificationException("Attempting to remove all logunit. " +
"No replicas available.");
}
loguintServers.remove(k);
} else {
k++;
}
}
}
}
layout.getSegments().retainAll(modifiedLayoutSegments);
return this;
}
/**
* Builds and returns the layout with the modified attributes.
*
* @return new layout
*/
public Layout build() {
return new Layout(
layout.getLayoutServers(),
layout.getSequencers(),
layout.getSegments(),
layout.getUnresponsiveServers(),
this.epoch);
}
}