/**
* VMware Continuent Tungsten Replicator
* Copyright (C) 2015 VMware, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Initial developer(s): Edward Archibald
* Contributor(s): Robert Hodges
*/
package com.continuent.tungsten.common.cluster.resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import com.continuent.tungsten.common.exception.ClusterMembershipValidationException;
import com.continuent.tungsten.common.utils.CLUtils;
/**
* Encapsulates the logic to determine whether a set of members represents a
* primary partition that may continue to operate as a cluster. This class also
* includes logic to determine whether the current GC view is consistent and
* whether the digest itself is valid.
*/
public class ClusterMembershipDigest
{
// Base data.
private String name;
private Vector<String> configuredDBMembers = new Vector<String>();
private Vector<String> configuredActiveWitnesses = new Vector<String>();
private Vector<String> viewDBMembers = new Vector<String>();
private Vector<String> viewActiveWitnessMembers = new Vector<String>();
private Vector<String> consolidatedViewMembers = new Vector<String>();
private Vector<String> passiveWitnesses = new Vector<String>();
// Set consisting of union of all known members.
private HashMap<String, ClusterMember> potentialQuorumMembersSet = new HashMap<String, ClusterMember>();
// Witness host definition, if used.
private HashMap<String, ClusterMember> passsiveWitnessSet = new HashMap<String, ClusterMember>();
private StringBuilder decisionSteps = new StringBuilder();
private String conclusion = "UNKNOWN";
/**
* Instantiates a digest used to compute whether the member that creates the
* digest is in a primary group.
*
* @param name Name of this member
* @param configuredDBMembers Member names from service configuration
* @param configuredActiveWitnesses Member names of active witnesses, if any
* @param viewDBMembers Member names from group communications view that are
* DB members
* @param viewActiveWitnessMembers Active witnesses that appear in the view
* provided by group communications
* @param passiveWitnesses Names of the witness hosts
*/
public ClusterMembershipDigest(String name,
Collection<String> configuredDBMembers,
Collection<String> configuredActiveWitnesses,
Collection<String> viewDBMembers,
Collection<String> viewActiveWitnessMembers,
List<String> passiveWitnesses)
{
// Assign values.
this.name = name;
if (configuredDBMembers != null)
{
this.configuredDBMembers.addAll(configuredDBMembers);
}
if (configuredActiveWitnesses != null
&& configuredActiveWitnesses.size() > 0)
{
this.configuredActiveWitnesses.addAll(configuredActiveWitnesses);
}
if (viewDBMembers != null)
{
this.viewDBMembers.addAll(viewDBMembers);
this.consolidatedViewMembers.addAll(viewDBMembers);
}
if (viewActiveWitnessMembers != null)
{
this.viewActiveWitnessMembers.addAll(viewActiveWitnessMembers);
this.consolidatedViewMembers.addAll(viewActiveWitnessMembers);
}
if (passiveWitnesses != null)
{
this.passiveWitnesses.addAll(passiveWitnesses);
}
// Construct quorum set.
derivePotentialQuorumMembersSet();
}
/*
* Construct the set of all members, which is the union of the configured
* and view members, that could potentially be in the quorum set.
*/
private void derivePotentialQuorumMembersSet()
{
// Add configured members first.
for (String name : configuredDBMembers)
{
ClusterMember cm = new ClusterMember(name);
cm.setConfigured(true);
cm.setDbMember(true);
potentialQuorumMembersSet.put(name, cm);
}
if (configuredActiveWitnesses != null
&& configuredActiveWitnesses.size() > 0)
{
// Also add, if they exist, active witnesses.
for (String name : configuredActiveWitnesses)
{
ClusterMember cm = new ClusterMember(name);
cm.setConfigured(true);
cm.setActiveWitness(true);
potentialQuorumMembersSet.put(name, cm);
}
}
/*
* Now iterate across the view DB members and add new member definitions
* or update existing ones.
*/
for (String name : viewDBMembers)
{
ClusterMember cm = potentialQuorumMembersSet.get(name);
if (cm == null)
{
cm = new ClusterMember(name);
cm.setInView(true);
potentialQuorumMembersSet.put(name, cm);
}
else
{
cm.setInView(true);
}
}
/*
* Now iterate across the view active witness members and add new member
* definitions or update existing ones.
*/
for (String name : viewActiveWitnessMembers)
{
ClusterMember cm = potentialQuorumMembersSet.get(name);
if (cm == null)
{
cm = new ClusterMember(name);
cm.setInView(true);
potentialQuorumMembersSet.put(name, cm);
}
else
{
cm.setInView(true);
}
}
// Add the witness hosts if we have any.
for (String name : passiveWitnesses)
{
ClusterMember witness = new ClusterMember(name);
witness.setPassiveWitness(true);
passsiveWitnessSet.put(name, witness);
potentialQuorumMembersSet.put(name, witness);
}
}
/** Return name of current member. */
public String getName()
{
return name;
}
/**
* Return the number of members required to have a simple majority. We don't
* count either active or passive witnesses when looking for a simple
* majority but simply the number of DB members.
*/
public int getSimpleMajoritySize()
{
return ((potentialQuorumMembersSet.size()
- passiveWitnessesInQuorumSetCount() - activeWitnessesInQuorumSetCount()) / 2 + 1);
}
/**
* Sets the validation flag on a member.
*
* @param member Name of the member that was tested
* @param valid If true member was validated through GC ping
*/
public void setValidated(String member, boolean valid)
throws ClusterMembershipValidationException
{
ClusterMember cm = potentialQuorumMembersSet.get(member);
if (cm == null)
{
throw new ClusterMembershipValidationException(
String.format(
"Cannot validate member '%s' because it does not appear in the potential quorum member set.",
member));
}
if (!consolidatedViewMembers.contains(cm.getName()))
{
throw new ClusterMembershipValidationException(
String.format(
"Cannot validate member '%s' because it does not appear in the view.",
cm.getName()));
}
if (cm != null)
{
if (cm.getValidated() == valid)
{
return;
}
cm.setValidated(valid);
}
}
/**
* Sets the reachability flag on a member.
*
* @param member Name of the member that was tested
* @param reached If true member was reached with a network ping command
*/
public void setReachable(String member, boolean reached)
{
ClusterMember cm = potentialQuorumMembersSet.get(member);
if (cm != null)
{
if (cm.getReachable() == reached)
return;
cm.setReachable(reached);
}
else
{
ClusterMember witness = passsiveWitnessSet.get(member);
if (member.equals(witness.getName()))
{
if (witness.getReachable() == reached)
return;
witness.setReachable(reached);
}
}
}
/**
* Return quorum set members.
*/
public List<ClusterMember> getPotentialQuorumMembersSet()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
potentialQuorumMembersSet.size());
list.addAll(potentialQuorumMembersSet.values());
return list;
}
/**
* Return definitions of the configured members.
*/
public List<ClusterMember> getConsolidatedConfiguredMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
configuredDBMembers.size() + configuredActiveWitnesses.size());
for (String name : configuredDBMembers)
{
list.add(potentialQuorumMembersSet.get(name));
}
for (String name : configuredActiveWitnesses)
{
list.add(potentialQuorumMembersSet.get(name));
}
return list;
}
/**
* Return definitions of the configured members.
*/
public List<ClusterMember> getConfiguredDBSetMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
configuredDBMembers.size());
for (String name : configuredDBMembers)
{
list.add(potentialQuorumMembersSet.get(name));
}
return list;
}
/**
* Return definitions of the configured members.
*/
public List<ClusterMember> getConfiguredActiveWitnessSetMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
configuredActiveWitnesses.size());
for (String name : configuredActiveWitnesses)
{
list.add(potentialQuorumMembersSet.get(name));
}
return list;
}
/**
* Return definitions of the view members.
*/
public List<ClusterMember> getViewDBSetMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
viewDBMembers.size());
for (String name : viewDBMembers)
{
list.add(potentialQuorumMembersSet.get(name));
}
return list;
}
/**
* Return definitions of the view members.
*/
public List<ClusterMember> getViewActiveWitnessSetMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
viewActiveWitnessMembers.size());
for (String name : viewActiveWitnessMembers)
{
list.add(potentialQuorumMembersSet.get(name));
}
return list;
}
/**
* Return definitions of the witness members.
*/
public List<ClusterMember> getWitnessSetMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
passsiveWitnessSet.size());
for (ClusterMember cm : passsiveWitnessSet.values())
{
list.add(cm);
}
return list;
}
/**
* Return the validated members.
*/
public List<ClusterMember> getValidatedMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
potentialQuorumMembersSet.size());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
// Validated members must have been checked *and* must have
// a true value.
Boolean valid = cm.getValidated();
if (valid != null && valid)
list.add(cm);
}
return list;
}
/**
* Return the validated members.
*/
public List<ClusterMember> getValidatedDBMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
getPotentialQuorumMembersSet().size());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
if (cm.isActiveWitness() || cm.isPassiveWitness())
{
continue;
}
// Validated members must have been checked *and* must have
// a true value.
Boolean valid = cm.getValidated();
if (valid != null && valid)
list.add(cm);
}
return list;
}
/**
* Return the validated members.
*/
public List<ClusterMember> getValidatedActiveWitnessMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
activeWitnessesInQuorumSetCount());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
if (!cm.isActiveWitness())
{
continue;
}
// Validated members must have been checked *and* must have
// a true value.
Boolean valid = cm.getValidated();
if (valid != null && valid)
list.add(cm);
}
return list;
}
/**
* Return the reachable members.
*/
public List<ClusterMember> getReachableMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
potentialQuorumMembersSet.size());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
// Reachable members must have been checked *and* must have
// a true value.
Boolean reachable = cm.getReachable();
if (reachable != null && reachable)
list.add(cm);
}
return list;
}
/**
* Return the reachable members.
*/
public List<ClusterMember> getReachableDBMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
validatedDBMembersInQuorumSetCount());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
if (cm.isActiveWitness() || cm.isPassiveWitness())
{
continue;
}
// Reachable members must have been checked *and* must have
// a true value.
Boolean reachable = cm.getReachable();
if (reachable != null && reachable)
list.add(cm);
}
return list;
}
/**
* Return the reachable members.
*/
public List<ClusterMember> getReachableActiveWitnessMembers()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
activeWitnessesInQuorumSetCount());
for (ClusterMember cm : potentialQuorumMembersSet.values())
{
if (!cm.isActiveWitness())
{
continue;
}
// Reachable members must have been checked *and* must have
// a true value.
Boolean reachable = cm.getReachable();
if (reachable != null && reachable)
list.add(cm);
}
return list;
}
/**
* Return the reachable witnesses.
*/
public List<ClusterMember> getReachablePassiveWitnesses()
{
ArrayList<ClusterMember> list = new ArrayList<ClusterMember>(
reachablePassiveWitnessesInQuorumSetCount());
for (ClusterMember cm : passsiveWitnessSet.values())
{
// Reachable witnesses must have been checked *and* must have
// a true value.
Boolean reachable = cm.getReachable();
if (reachable != null && reachable)
list.add(cm);
}
return list;
}
/** Return member names from the quorum set. */
public List<String> getPotentialQuorumMembersSetNames()
{
return clusterMembersToNames(potentialQuorumMembersSet.values());
}
/** Return validated member names. */
public List<String> getValidatedMemberNames()
{
return clusterMembersToNames(getValidatedMembers());
}
/** Return validated member names. */
public List<String> getValidatedDBMemberNames()
{
return clusterMembersToNames(getValidatedDBMembers());
}
/** Return validated member names. */
public List<String> getValidatedActiveWitnessMemberNames()
{
return clusterMembersToNames(getValidatedActiveWitnessMembers());
}
/** Return reachable member names. */
public List<String> getReachableDBMemberNames()
{
return clusterMembersToNames(getReachableDBMembers());
}
/** Return reachable member names. */
public List<String> getReachableActiveWitnessMemberNames()
{
return clusterMembersToNames(getReachableActiveWitnessMembers());
}
/** Return reachable member names. */
public List<String> getReachableMemberNames()
{
return clusterMembersToNames(getReachableMembers());
}
/** Return reachable witness names. */
public List<String> getReachableWitnessNames()
{
return clusterMembersToNames(getReachablePassiveWitnesses());
}
// Conversion routine.
private List<String> clusterMembersToNames(Collection<ClusterMember> members)
{
ArrayList<String> list = new ArrayList<String>(members.size());
for (ClusterMember member : members)
{
list.add(member.getName());
}
return list;
}
/**
* Test to see if we have a valid set of potential quorum members. This
* checks a number of conditions that if violated indicate that the manager
* is either misconfigured or group communications is misbehaving, which in
* turn could lead to an invalid computation of quorum.
*
* @return Returns true if the
*/
public boolean isValidPotentialQuorumMembersSet(boolean verbose)
{
if (configuredDBMembers.size() == 0)
{
// The quorum set must contain at least one configured member.
if (verbose)
{
logDecisionStep(verbose,
"INVALID POTENTIAL QUORUM MEMBERS SET: NO CONFIGURED MEMBERS FOUND");
logDecisionStep(verbose,
"(ENSURE THAT dataservices.properties FILE CONTAINS AT LEAST MEMBER "
+ name + ")");
}
return false;
}
else if (viewDBMembers.size() == 0)
{
// The quorum set must contain at least one member in the GC view.
if (verbose)
{
logDecisionStep(
verbose,
"INVALID POTENTIAL QUORUM MEMBERS SET: GROUP COMMUNICATION VIEW CONTAINS NO MEMBERS");
logDecisionStep(verbose,
"(GROUP COMMUNICATIONS MAY BE MISCONFIGURED OR BLOCKED BY A FIREWALL)");
}
return false;
}
else if (potentialQuorumMembersSet.get(name) == null)
{
// The quorum set must contain the current member.
if (verbose)
{
logDecisionStep(verbose,
"INVALID POTENTIAL QUORUM MEMBERS SET: THIS MEMBER "
+ name + " IS NOT LISTED");
logDecisionStep(
verbose,
"(GROUP COMMUNICATIONS MAY BE MISCONFIGURED OR BLOCKED BY A FIREWALL; MEMBER NAME MAY BE MISSING FROM dataservices.properties)");
}
return false;
}
else if (!potentialQuorumMembersSet.get(name).isInView())
{
// The member must be in the group communications view.
if (verbose)
{
logDecisionStep(
verbose,
"INVALID POTENTIAL QUORUM MEMBERS SET: THIS MEMBER "
+ name
+ " IS NOT LISTED IN THE GROUP COMMUNICATION VIEW");
logDecisionStep(verbose,
"(GROUP COMMUNICATIONS MAY BE MISCONFIGURED OR BLOCKED BY A FIREWALL)");
}
return false;
}
else
{
// This quorum set appears valid.
return true;
}
}
/**
* Determines whether the local manager is in a primary partition, based on
* validated membership information passed in when this class is
* instantiated. A manager is in a primary partition if one of the following
* conditions is met.
* <ul>
* <li>The quorum set is one and contains the current member</li>
* <li>The quorum set contains a simple majority of validated members</li>
* <li>The quorum set contains an even number of validated members with
* reachable witness hosts (all must be reachable)</li>
* </ul>
* If none of the above obtains, the manager is not in a primary partition.
*
* @param verbose Logs information about how the determination is being
* made.
* @return true if we are in a primary partition
*/
public boolean isInPrimaryPartition(boolean verbose)
{
int simpleMajority = this.getSimpleMajoritySize();
// Print a message to explain what we are doing.
if (verbose)
{
logDecisionStep(verbose,
"========================================================================");
if (passiveWitnessesInQuorumSetCount() > 0)
{
logDecisionStep(
verbose,
String.format(
"CHECKING FOR QUORUM: MUST BE AT LEAST %d DB MEMBERS %s",
simpleMajority,
simpleMajority > 1
? String.format(
"OR %d MEMBERS PLUS ALL %d PASSIVE WITNESSES",
simpleMajority - 1,
passiveWitnessesInQuorumSetCount())
: ""));
}
else if (activeWitnessesInQuorumSetCount() > 0)
{
logDecisionStep(
verbose,
String.format(
"CHECKING FOR QUORUM: MUST BE AT LEAST %d DB MEMBERS %s",
simpleMajority,
simpleMajority > 1
? String.format(
"OR %d DB MEMBERS PLUS AT LEAST 1 ACTIVE WITNESS",
simpleMajority - 1)
: ""));
}
else
{
logDecisionStep(verbose, String.format(
"CHECKING FOR QUORUM: MUST BE AT LEAST %d DB MEMBERS",
simpleMajority));
}
logDecisionStep(
verbose,
"QUORUM SET MEMBERS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getPotentialQuorumMembersSetNames()));
logDecisionStep(verbose,
"SIMPLE MAJORITY SIZE: " + this.getSimpleMajoritySize());
logDecisionStep(verbose, "GC VIEW OF CURRENT DB MEMBERS IS: "
+ CLUtils.iterableToCommaSeparatedList(viewDBMembers));
logDecisionStep(
verbose,
"VALIDATED DB MEMBERS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getValidatedDBMemberNames()));
logDecisionStep(
verbose,
"REACHABLE DB MEMBERS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getReachableDBMemberNames()));
if (activeWitnessesInQuorumSetCount() > 0)
{
logDecisionStep(
verbose,
"GC VIEW OF CURRENT ACTIVE WITNESS MEMBERS IS: "
+ CLUtils
.iterableToCommaSeparatedList(viewDBMembers));
logDecisionStep(
verbose,
"VALIDATED ACTIVE WITNESS MEMBERS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getValidatedActiveWitnessMemberNames()));
logDecisionStep(
verbose,
"REACHABLE ACTIVE WITNESS MEMBERS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getReachableActiveWitnessMemberNames()));
}
if (passiveWitnesses.size() > 0)
{
logDecisionStep(
verbose,
"WITNESS HOSTS ARE: "
+ CLUtils
.iterableToCommaSeparatedList(passiveWitnesses));
logDecisionStep(
verbose,
"REACHABLE WITNESSES ARE: "
+ CLUtils
.iterableToCommaSeparatedList(getReachableWitnessNames()));
}
logDecisionStep(verbose,
"========================================================================");
}
// Ensure the quorum set is valid.
if (!this.isValidPotentialQuorumMembersSet(verbose))
{
logDecisionStep(
verbose,
"CONCLUSION: UNABLE TO ESTABLISH MAJORITY DUE TO INVALID POTENTIAL QUORUM MEMBERS SET");
return false;
}
if (!this.isValidMembership(verbose))
{
logDecisionStep(verbose, "CONCLUSION: MEMBERSHIP IS INVALID");
return false;
}
/*
* If we have a valid quorum set with a single validated member, then we
* have a primary partition. This case covers a cluster with a single
* master.
*/
if (potentialQuorumMembersSet.size() == 1
&& validatedDBMembersInQuorumSetCount() == 1)
{
logDecisionStep(
verbose,
"CONCLUSION: I AM IN A PRIMARY PARTITION AS THERE IS A SINGLE VALIDATED MEMBER IN THE QUORUM SET");
return true;
}
// If we have a simple majority of validated members in the quorum set,
// then we have a primary partition.
if (validatedDBMembersInQuorumSetCount() >= simpleMajority)
{
logDecisionStep(
verbose,
String.format(
"CONCLUSION: I AM IN A PRIMARY PARTITION OF %d DB MEMBERS OUT OF THE REQUIRED MAJORITY OF %d",
validatedDBMembersInQuorumSetCount(),
simpleMajority));
return true;
}
/*
* By the time we get here, 'validated' should be equal to 'viewMembers'
* since we will return the fact that the the potential quorum members
* set is invalid if they are not. So the test that uses 'validated'
* below should be sufficient to indicated that all members can be seen.
* If we are shy of a majority by one member, we can use witnesses, if
* they exist, to break the tie. But the key is that if there is more
* than one witness, we need to see ALL of the witnesses. Otherwise we
* could end up with a partition in which one partition sees one witness
* and the other partition sees another etc.
*/
if (validatedDBMembersInQuorumSetCount() >= simpleMajority - 1)
{
boolean passiveWitnessesOK = passsiveWitnessSet.size() > 0
&& (passsiveWitnessSet.size() == reachablePassiveWitnessesInQuorumSetCount());
if (passiveWitnessesOK)
{
logDecisionStep(
verbose,
String.format(
"CONCLUSION: I AM IN A PRIMARY PARTITION OF %d MEMBERS, WITH %d VALIDATED DB MEMBERS AND ALL (%d) REACHABLE PASSIVE WITNESSES",
simpleMajority,
validatedDBMembersInQuorumSetCount(),
reachablePassiveWitnessesInQuorumSetCount()));
return true;
}
else
{
/*
* If we have active witnesses, check them now....
*/
int validatedActiveWitneses = validatedActiveWitnessesInQuorumSetCount();
if (validatedDBMembersInQuorumSetCount()
+ validatedActiveWitneses >= simpleMajority)
{
logDecisionStep(
verbose,
String.format(
"CONCLUSION: I AM IN A PRIMARY PARTITION WITH %d VALIDATED DB MEMBERS AND %d VALIDATED ACTIVE WITNESSES",
validatedDBMembersInQuorumSetCount(),
validatedActiveWitneses));
return true;
}
else
{
logDecisionStep(
verbose,
String.format(
"CONCLUSION: I AM IN A NON-PRIMARY PARTITION OF %d MEMBERS OUT OF A REQUIRED MAJORITY SIZE OF %d\n"
+ "AND THERE ARE %d REACHABLE WITNESSES OUT OF %d",
validatedDBMembersInQuorumSetCount(),
getSimpleMajoritySize(),
reachablePassiveWitnessesInQuorumSetCount(),
passsiveWitnessSet.size()));
return false;
}
}
}
/*
* We cannot form a quorum. Provide an explanation if desired.
*/
if (verbose)
{
logDecisionStep(
verbose,
String.format(
"CONCLUSION: I AM IN A NON-PRIMARY PARTITION OF %d MEMBERS OUT OF A REQUIRED MAJORITY SIZE OF %d\n",
validatedDBMembersInQuorumSetCount(),
getSimpleMajoritySize()));
}
return false;
}
/**
* Returns true if the group membership is valid, which is the case if the
* following conditions obtain:
* <ul>
* <li>There is at least 1 member in the group</li>
* <li>All individual members in the group are validated through a ping</li>
* </ul>
*/
public boolean isValidMembership(boolean verbose)
{
/*
* This is a case where we are looking for consistency between the total
* view that we see via GCS and the members that we can validate. So
* here we do not treat active witnesses any differently because what we
* are testing for is, essentially, consistency between what GCS sees
* and the members that are reachable. If we don't have a consistent
* view, then there's possibly a network partition in effect etc.
*/
if (consolidatedViewMembers.size() > 0
&& getValidatedMembers().size() > 0)
{
if (setsAreEqual(consolidatedViewMembers, getValidatedMembers()))
{
if (verbose)
{
logDecisionStep(verbose,
"MEMBERSHIP IS VALID BASED ON VIEW/VALIDATED CONSOLIDATED MEMBERS CONSISTENCY");
}
return true;
}
}
if (verbose)
{
logDecisionStep(
verbose,
String.format(
"MEMBERSHIP IS NOT VALID: %d MEMBERS APPEAR IN VIEW AND ONLY %d CAN BE VALIDATED",
consolidatedViewMembers.size(),
getValidatedMembers().size()));
}
return false;
}
private boolean setsAreEqual(List<String> viewSet,
List<ClusterMember> targetSet)
{
int hitCount = 0;
for (String viewMember : viewSet)
{
for (ClusterMember member : targetSet)
{
if (member.getName().equals(viewMember))
hitCount++;
}
}
return (hitCount == viewSet.size());
}
public Vector<String> getConfiguredActiveWitnesses()
{
return configuredActiveWitnesses;
}
public void setConfiguredActiveWitnesses(
Vector<String> configuredActiveWitnesses)
{
this.configuredActiveWitnesses = configuredActiveWitnesses;
}
/**
* Determine how many active witnesses are in the potential quorum set.
*/
public int activeWitnessesInQuorumSetCount()
{
int activeWitnessCount = 0;
for (ClusterMember member : potentialQuorumMembersSet.values())
{
if (member.isActiveWitness())
{
activeWitnessCount++;
}
}
return activeWitnessCount;
}
public int validatedActiveWitnessesInQuorumSetCount()
{
int validatedActiveWitnessCount = 0;
for (ClusterMember member : potentialQuorumMembersSet.values())
{
if (member.isActiveWitness() && member.getValidated())
{
validatedActiveWitnessCount++;
}
}
return validatedActiveWitnessCount;
}
public int reachablePassiveWitnessesInQuorumSetCount()
{
int count = 0;
for (ClusterMember member : potentialQuorumMembersSet.values())
{
if (member.isPassiveWitness() && member.getReachable())
{
count++;
}
}
return count;
}
public int validatedDBMembersInQuorumSetCount()
{
int validatedDBMemberCount = 0;
for (ClusterMember member : potentialQuorumMembersSet.values())
{
if (member.isDbMember() && member.getValidated())
{
validatedDBMemberCount++;
}
}
return validatedDBMemberCount;
}
/**
* Determine how many passive witnesses are in the potential quorum set.
*/
public int passiveWitnessesInQuorumSetCount()
{
int passiveWitnessCount = 0;
for (ClusterMember member : potentialQuorumMembersSet.values())
{
if (member.isPassiveWitness())
{
passiveWitnessCount++;
}
}
return passiveWitnessCount;
}
public Vector<String> getViewActiveWitnessMembers()
{
return viewActiveWitnessMembers;
}
public void setViewActiveWitnessMembers(
Vector<String> viewActiveWitnessMembers)
{
this.viewActiveWitnessMembers = viewActiveWitnessMembers;
}
public Vector<String> getConsolidatedViewMembers()
{
return consolidatedViewMembers;
}
public void setConsolidatedViewMembers(
Vector<String> consolidatedViewMembers)
{
this.consolidatedViewMembers = consolidatedViewMembers;
}
private void logDecisionStep(boolean verbose, String step)
{
decisionSteps.append(step).append("\n");
if (step.startsWith("CONCLUSION"))
{
setConclusion(step);
}
if (verbose)
{
CLUtils.println(step);
}
}
public String getDecisionSteps()
{
return decisionSteps.toString();
}
public String getConclusion()
{
return conclusion;
}
public void setConclusion(String conclusion)
{
this.conclusion = conclusion;
}
public boolean isValidated(String name)
throws ClusterMembershipValidationException
{
ClusterMember cm = potentialQuorumMembersSet.get(name);
if (cm == null)
{
throw new ClusterMembershipValidationException(
String.format(
"Member '%s' does not exist in the potential quorum member set",
name));
}
return cm.getValidated();
}
}