Fixed build
This commit is contained in:
@ -21,10 +21,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.gossip.lock.LockManagerSettings;
|
import org.apache.gossip.lock.LockManagerSettings;
|
||||||
|
|
||||||
/**
|
/** In this object the settings used by the GossipService are held. */
|
||||||
* In this object the settings used by the GossipService are held.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class GossipSettings {
|
public class GossipSettings {
|
||||||
|
|
||||||
/** Time between gossip'ing in ms. Default is 1 second. */
|
/** Time between gossip'ing in ms. Default is 1 second. */
|
||||||
@ -35,58 +32,59 @@ public class GossipSettings {
|
|||||||
|
|
||||||
/** the minimum samples needed before reporting a result */
|
/** the minimum samples needed before reporting a result */
|
||||||
private int minimumSamples = 5;
|
private int minimumSamples = 5;
|
||||||
|
|
||||||
/** the number of samples to keep per host */
|
/** the number of samples to keep per host */
|
||||||
private int windowSize = 5000;
|
private int windowSize = 5000;
|
||||||
|
|
||||||
/** the threshold for the detector */
|
/** the threshold for the detector */
|
||||||
private double convictThreshold = 10;
|
private double convictThreshold = 10;
|
||||||
|
|
||||||
private String distribution = "normal";
|
private String distribution = "normal";
|
||||||
|
|
||||||
private String activeGossipClass = "org.apache.gossip.manager.SimpleActiveGossiper";
|
private String activeGossipClass = "org.apache.gossip.manager.SimpleActiveGossiper";
|
||||||
|
|
||||||
private String transportManagerClass = "org.apache.gossip.transport.udp.UdpTransportManager";
|
private String transportManagerClass = "org.apache.gossip.transport.udp.UdpTransportManager";
|
||||||
private String protocolManagerClass = "org.apache.gossip.protocol.json.JacksonProtocolManager";
|
private String protocolManagerClass = "org.apache.gossip.protocol.json.JacksonProtocolManager";
|
||||||
|
|
||||||
private Map<String,String> activeGossipProperties = new HashMap<>();
|
private Map<String, String> activeGossipProperties = new HashMap<>();
|
||||||
|
|
||||||
private String pathToRingState = "./";
|
private String pathToRingState = "./";
|
||||||
|
|
||||||
private boolean persistRingState = true;
|
private boolean persistRingState = true;
|
||||||
|
|
||||||
private String pathToDataState = "./";
|
private String pathToDataState = "./";
|
||||||
|
|
||||||
private boolean persistDataState = true;
|
private boolean persistDataState = true;
|
||||||
|
|
||||||
private String pathToKeyStore = "./keys";
|
private String pathToKeyStore = "./keys";
|
||||||
|
|
||||||
private boolean signMessages = false;
|
private boolean signMessages = false;
|
||||||
|
|
||||||
// Settings related to lock manager
|
// Settings related to lock manager
|
||||||
private LockManagerSettings lockManagerSettings = LockManagerSettings
|
private LockManagerSettings lockManagerSettings =
|
||||||
.getLockManagerDefaultSettings();
|
LockManagerSettings.getLockManagerDefaultSettings();
|
||||||
|
|
||||||
private boolean bulkTransfer = false;
|
private boolean bulkTransfer = false;
|
||||||
|
|
||||||
private int bulkTransferSize = StartupSettings.DEFAULT_BULK_TRANSFER_SIZE;
|
private int bulkTransferSize = StartupSettings.DEFAULT_BULK_TRANSFER_SIZE;
|
||||||
|
|
||||||
/**
|
/** Construct GossipSettings with default settings. */
|
||||||
* Construct GossipSettings with default settings.
|
public GossipSettings() {}
|
||||||
*/
|
|
||||||
public GossipSettings() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct GossipSettings with given settings.
|
* Construct GossipSettings with given settings.
|
||||||
*
|
*
|
||||||
* @param gossipInterval
|
* @param gossipInterval The gossip interval in ms.
|
||||||
* The gossip interval in ms.
|
* @param cleanupInterval The cleanup interval in ms.
|
||||||
* @param cleanupInterval
|
|
||||||
* The cleanup interval in ms.
|
|
||||||
*/
|
*/
|
||||||
public GossipSettings(int gossipInterval, int cleanupInterval, int windowSize, int minimumSamples,
|
public GossipSettings(
|
||||||
double convictThreshold, String distribution, boolean bulkTransfer) {
|
int gossipInterval,
|
||||||
|
int cleanupInterval,
|
||||||
|
int windowSize,
|
||||||
|
int minimumSamples,
|
||||||
|
double convictThreshold,
|
||||||
|
String distribution,
|
||||||
|
boolean bulkTransfer) {
|
||||||
this.gossipInterval = gossipInterval;
|
this.gossipInterval = gossipInterval;
|
||||||
this.cleanupInterval = cleanupInterval;
|
this.cleanupInterval = cleanupInterval;
|
||||||
this.windowSize = windowSize;
|
this.windowSize = windowSize;
|
||||||
@ -98,9 +96,8 @@ public class GossipSettings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the gossip interval. This is the time between a gossip message is send.
|
* Set the gossip interval. This is the time between a gossip message is send.
|
||||||
*
|
*
|
||||||
* @param gossipInterval
|
* @param gossipInterval The gossip interval in ms.
|
||||||
* The gossip interval in ms.
|
|
||||||
*/
|
*/
|
||||||
public void setGossipTimeout(int gossipInterval) {
|
public void setGossipTimeout(int gossipInterval) {
|
||||||
this.gossipInterval = gossipInterval;
|
this.gossipInterval = gossipInterval;
|
||||||
@ -121,7 +118,7 @@ public class GossipSettings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the clean interval.
|
* Get the clean interval.
|
||||||
*
|
*
|
||||||
* @return The cleanup interval.
|
* @return The cleanup interval.
|
||||||
*/
|
*/
|
||||||
public int getCleanupInterval() {
|
public int getCleanupInterval() {
|
||||||
@ -132,8 +129,7 @@ public class GossipSettings {
|
|||||||
* Set the cleanup interval. This is the time between the last heartbeat received from a member
|
* Set the cleanup interval. This is the time between the last heartbeat received from a member
|
||||||
* and when it will be marked as dead.
|
* and when it will be marked as dead.
|
||||||
*
|
*
|
||||||
* @param cleanupInterval
|
* @param cleanupInterval The cleanup interval in ms.
|
||||||
* The cleanup interval in ms.
|
|
||||||
*/
|
*/
|
||||||
public void setCleanupInterval(int cleanupInterval) {
|
public void setCleanupInterval(int cleanupInterval) {
|
||||||
this.cleanupInterval = cleanupInterval;
|
this.cleanupInterval = cleanupInterval;
|
||||||
@ -257,6 +253,7 @@ public class GossipSettings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the lock settings use by the lock manager
|
* Set the lock settings use by the lock manager
|
||||||
|
*
|
||||||
* @param lockManagerSettings lock settings. This object cannot be null.
|
* @param lockManagerSettings lock settings. This object cannot be null.
|
||||||
*/
|
*/
|
||||||
public void setLockManagerSettings(LockManagerSettings lockManagerSettings) {
|
public void setLockManagerSettings(LockManagerSettings lockManagerSettings) {
|
||||||
|
@ -19,7 +19,6 @@ package org.apache.gossip;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.gossip.accrual.FailureDetector;
|
import org.apache.gossip.accrual.FailureDetector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,34 +32,33 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** This object represents the settings used when starting the gossip service. */
|
||||||
* This object represents the settings used when starting the gossip service.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class StartupSettings {
|
public class StartupSettings {
|
||||||
|
|
||||||
public static final int DEFAULT_BULK_TRANSFER_SIZE = 100;
|
public static final int DEFAULT_BULK_TRANSFER_SIZE = 100;
|
||||||
|
|
||||||
/** Default setting values */
|
/** Default setting values */
|
||||||
private static final boolean DEFAULT_BULK_TRANSFER = false;
|
private static final boolean DEFAULT_BULK_TRANSFER = false;
|
||||||
|
|
||||||
/** The gossip settings used at startup. */
|
/** The gossip settings used at startup. */
|
||||||
private final GossipSettings gossipSettings;
|
private final GossipSettings gossipSettings;
|
||||||
|
|
||||||
/** The list with gossip members to start with. */
|
/** The list with gossip members to start with. */
|
||||||
private final List<Member> gossipMembers;
|
private final List<Member> gossipMembers;
|
||||||
|
|
||||||
/** The id to use fo the service */
|
/** The id to use fo the service */
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
private URI uri;
|
private URI uri;
|
||||||
private String cluster;
|
private String cluster;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id The id to be used for this service
|
||||||
* The id to be used for this service
|
* @param uri A URI object containing IP/hostname and port
|
||||||
* @param uri
|
* @param logLevel unused
|
||||||
* A URI object containing IP/hostname and port
|
|
||||||
* @param logLevel
|
|
||||||
* unused
|
|
||||||
*/
|
*/
|
||||||
public StartupSettings(String id, URI uri, int logLevel, String cluster) {
|
public StartupSettings(String id, URI uri, int logLevel, String cluster) {
|
||||||
this(id, uri, new GossipSettings(), cluster);
|
this(id, uri, new GossipSettings(), cluster);
|
||||||
@ -68,10 +67,8 @@ public class StartupSettings {
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id The id to be used for this service
|
||||||
* The id to be used for this service
|
* @param uri A URI object containing IP/hostname and port
|
||||||
* @param uri
|
|
||||||
* A URI object containing IP/hostname and port
|
|
||||||
*/
|
*/
|
||||||
public StartupSettings(String id, URI uri, GossipSettings gossipSettings, String cluster) {
|
public StartupSettings(String id, URI uri, GossipSettings gossipSettings, String cluster) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -84,30 +81,27 @@ public class StartupSettings {
|
|||||||
/**
|
/**
|
||||||
* Parse the settings for the gossip service from a JSON file.
|
* Parse the settings for the gossip service from a JSON file.
|
||||||
*
|
*
|
||||||
* @param jsonFile
|
* @param jsonFile The file object which refers to the JSON config file.
|
||||||
* The file object which refers to the JSON config file.
|
|
||||||
* @return The StartupSettings object with the settings from the config file.
|
* @return The StartupSettings object with the settings from the config file.
|
||||||
* @throws FileNotFoundException
|
* @throws FileNotFoundException Thrown when the file cannot be found.
|
||||||
* Thrown when the file cannot be found.
|
* @throws IOException Thrown when reading the file gives problems.
|
||||||
* @throws IOException
|
|
||||||
* Thrown when reading the file gives problems.
|
|
||||||
* @throws URISyntaxException
|
* @throws URISyntaxException
|
||||||
*/
|
*/
|
||||||
public static StartupSettings fromJSONFile(File jsonFile) throws
|
public static StartupSettings fromJSONFile(File jsonFile)
|
||||||
FileNotFoundException, IOException, URISyntaxException {
|
throws FileNotFoundException, IOException, URISyntaxException {
|
||||||
ObjectMapper om = new ObjectMapper();
|
ObjectMapper om = new ObjectMapper();
|
||||||
JsonNode root = om.readTree(jsonFile);
|
JsonNode root = om.readTree(jsonFile);
|
||||||
JsonNode jsonObject = root.get(0);
|
JsonNode jsonObject = root.get(0);
|
||||||
String uri = jsonObject.get("uri").textValue();
|
String uri = jsonObject.get("uri").textValue();
|
||||||
String id = jsonObject.get("id").textValue();
|
String id = jsonObject.get("id").textValue();
|
||||||
Map<String,String> properties = new HashMap<String,String>();
|
Map<String, String> properties = new HashMap<String, String>();
|
||||||
JsonNode n = jsonObject.get("properties");
|
JsonNode n = jsonObject.get("properties");
|
||||||
Iterator<Entry<String, JsonNode>> l = n.fields();
|
Iterator<Entry<String, JsonNode>> l = n.fields();
|
||||||
while (l.hasNext()){
|
while (l.hasNext()) {
|
||||||
Entry<String, JsonNode> i = l.next();
|
Entry<String, JsonNode> i = l.next();
|
||||||
properties.put(i.getKey(), i.getValue().asText());
|
properties.put(i.getKey(), i.getValue().asText());
|
||||||
}
|
}
|
||||||
//TODO constants as defaults?
|
// TODO constants as defaults?
|
||||||
// TODO setting keys as constants?
|
// TODO setting keys as constants?
|
||||||
int gossipInterval = jsonObject.get("gossip_interval").intValue();
|
int gossipInterval = jsonObject.get("gossip_interval").intValue();
|
||||||
int cleanupInterval = jsonObject.get("cleanup_interval").intValue();
|
int cleanupInterval = jsonObject.get("cleanup_interval").intValue();
|
||||||
@ -116,24 +110,35 @@ public class StartupSettings {
|
|||||||
double convictThreshold = jsonObject.get("convict_threshold").asDouble();
|
double convictThreshold = jsonObject.get("convict_threshold").asDouble();
|
||||||
String cluster = jsonObject.get("cluster").textValue();
|
String cluster = jsonObject.get("cluster").textValue();
|
||||||
String distribution = jsonObject.get("distribution").textValue();
|
String distribution = jsonObject.get("distribution").textValue();
|
||||||
boolean bulkTransfer = jsonObject.has("bulk_transfer") ?
|
boolean bulkTransfer =
|
||||||
jsonObject.get("bulk_transfer").booleanValue() :
|
jsonObject.has("bulk_transfer")
|
||||||
DEFAULT_BULK_TRANSFER;
|
? jsonObject.get("bulk_transfer").booleanValue()
|
||||||
int bulkTransferSize = jsonObject.has("bulk_transfer_size") ?
|
: DEFAULT_BULK_TRANSFER;
|
||||||
jsonObject.get("bulk_transfer_size").intValue() :
|
int bulkTransferSize =
|
||||||
DEFAULT_BULK_TRANSFER_SIZE;
|
jsonObject.has("bulk_transfer_size")
|
||||||
if (cluster == null){
|
? jsonObject.get("bulk_transfer_size").intValue()
|
||||||
|
: DEFAULT_BULK_TRANSFER_SIZE;
|
||||||
|
if (cluster == null) {
|
||||||
throw new IllegalArgumentException("cluster was null. It is required");
|
throw new IllegalArgumentException("cluster was null. It is required");
|
||||||
}
|
}
|
||||||
String transportClass = jsonObject.has("transport_manager_class") ?
|
String transportClass =
|
||||||
jsonObject.get("transport_manager_class").textValue() :
|
jsonObject.has("transport_manager_class")
|
||||||
null;
|
? jsonObject.get("transport_manager_class").textValue()
|
||||||
String protocolClass = jsonObject.has("protocol_manager_class") ?
|
: null;
|
||||||
jsonObject.get("protocol_manager_class").textValue() :
|
String protocolClass =
|
||||||
null;
|
jsonObject.has("protocol_manager_class")
|
||||||
|
? jsonObject.get("protocol_manager_class").textValue()
|
||||||
|
: null;
|
||||||
URI uri2 = new URI(uri);
|
URI uri2 = new URI(uri);
|
||||||
GossipSettings gossipSettings = new GossipSettings(gossipInterval, cleanupInterval, windowSize,
|
GossipSettings gossipSettings =
|
||||||
minSamples, convictThreshold, distribution, bulkTransfer);
|
new GossipSettings(
|
||||||
|
gossipInterval,
|
||||||
|
cleanupInterval,
|
||||||
|
windowSize,
|
||||||
|
minSamples,
|
||||||
|
convictThreshold,
|
||||||
|
distribution,
|
||||||
|
bulkTransfer);
|
||||||
gossipSettings.setBulkTransferSize(bulkTransferSize);
|
gossipSettings.setBulkTransferSize(bulkTransferSize);
|
||||||
if (transportClass != null) {
|
if (transportClass != null) {
|
||||||
gossipSettings.setTransportManagerClass(transportClass);
|
gossipSettings.setTransportManagerClass(transportClass);
|
||||||
@ -144,15 +149,15 @@ public class StartupSettings {
|
|||||||
StartupSettings settings = new StartupSettings(id, uri2, gossipSettings, cluster);
|
StartupSettings settings = new StartupSettings(id, uri2, gossipSettings, cluster);
|
||||||
String configMembersDetails = "Config-members [";
|
String configMembersDetails = "Config-members [";
|
||||||
JsonNode membersJSON = jsonObject.get("members");
|
JsonNode membersJSON = jsonObject.get("members");
|
||||||
for (JsonNode child : membersJSON) {
|
for (JsonNode child : membersJSON) {
|
||||||
URI uri3 = new URI(child.get("uri").textValue());
|
URI uri3 = new URI(child.get("uri").textValue());
|
||||||
RemoteMember member = new RemoteMember(child.get("cluster").asText(),
|
RemoteMember member =
|
||||||
uri3, "", 0, new HashMap<String, String>()
|
new RemoteMember(
|
||||||
);
|
child.get("cluster").asText(), uri3, "", 0, new HashMap<String, String>());
|
||||||
settings.addGossipMember(member);
|
settings.addGossipMember(member);
|
||||||
configMembersDetails += member.computeAddress();
|
configMembersDetails += member.computeAddress();
|
||||||
configMembersDetails += ", ";
|
configMembersDetails += ", ";
|
||||||
}
|
}
|
||||||
log.info(configMembersDetails + "]");
|
log.info(configMembersDetails + "]");
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
@ -175,7 +180,7 @@ public class StartupSettings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id for this service.
|
* Get the id for this service.
|
||||||
*
|
*
|
||||||
* @return the service's id.
|
* @return the service's id.
|
||||||
*/
|
*/
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -185,8 +190,7 @@ public class StartupSettings {
|
|||||||
/**
|
/**
|
||||||
* Set the id to be used for this service.
|
* Set the id to be used for this service.
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id The id for this service.
|
||||||
* The id for this service.
|
|
||||||
*/
|
*/
|
||||||
public void setId(String id) {
|
public void setId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -204,8 +208,7 @@ public class StartupSettings {
|
|||||||
/**
|
/**
|
||||||
* Add a gossip member to the list of members to start with.
|
* Add a gossip member to the list of members to start with.
|
||||||
*
|
*
|
||||||
* @param member
|
* @param member The member to add.
|
||||||
* The member to add.
|
|
||||||
*/
|
*/
|
||||||
public void addGossipMember(Member member) {
|
public void addGossipMember(Member member) {
|
||||||
gossipMembers.add(member);
|
gossipMembers.add(member);
|
||||||
|
@ -38,8 +38,7 @@ public class FailureDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the statistics based on the delta between the last
|
* Updates the statistics based on the delta between the last heartbeat and supplied time
|
||||||
* heartbeat and supplied time
|
|
||||||
*
|
*
|
||||||
* @param now the time of the heartbeat in milliseconds
|
* @param now the time of the heartbeat in milliseconds
|
||||||
*/
|
*/
|
||||||
@ -63,9 +62,13 @@ public class FailureDetector {
|
|||||||
if (distribution.equals("normal")) {
|
if (distribution.equals("normal")) {
|
||||||
double standardDeviation = descriptiveStatistics.getStandardDeviation();
|
double standardDeviation = descriptiveStatistics.getStandardDeviation();
|
||||||
standardDeviation = Math.max(standardDeviation, 0.1);
|
standardDeviation = Math.max(standardDeviation, 0.1);
|
||||||
probability = new NormalDistribution(descriptiveStatistics.getMean(), standardDeviation).cumulativeProbability(delta);
|
probability =
|
||||||
|
new NormalDistribution(descriptiveStatistics.getMean(), standardDeviation)
|
||||||
|
.cumulativeProbability(delta);
|
||||||
} else {
|
} else {
|
||||||
probability = new ExponentialDistribution(descriptiveStatistics.getMean()).cumulativeProbability(delta);
|
probability =
|
||||||
|
new ExponentialDistribution(descriptiveStatistics.getMean())
|
||||||
|
.cumulativeProbability(delta);
|
||||||
}
|
}
|
||||||
final double eps = 1e-12;
|
final double eps = 1e-12;
|
||||||
if (1 - probability < eps) {
|
if (1 - probability < eps) {
|
||||||
|
@ -37,105 +37,135 @@ import org.apache.gossip.replication.WhiteListReplicable;
|
|||||||
|
|
||||||
abstract class OrSetMixin<E> {
|
abstract class OrSetMixin<E> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
OrSetMixin(@JsonProperty("elements") Map<E, Set<UUID>> w, @JsonProperty("tombstones") Map<E, Set<UUID>> h) { }
|
OrSetMixin(
|
||||||
@JsonProperty("elements") abstract Map<E, Set<UUID>> getElements();
|
@JsonProperty("elements") Map<E, Set<UUID>> w,
|
||||||
@JsonProperty("tombstones") abstract Map<E, Set<UUID>> getTombstones();
|
@JsonProperty("tombstones") Map<E, Set<UUID>> h) {}
|
||||||
@JsonIgnore abstract boolean isEmpty();
|
|
||||||
|
@JsonProperty("elements")
|
||||||
|
abstract Map<E, Set<UUID>> getElements();
|
||||||
|
|
||||||
|
@JsonProperty("tombstones")
|
||||||
|
abstract Map<E, Set<UUID>> getTombstones();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
abstract boolean isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class LWWSetMixin<ElementType> {
|
abstract class LWWSetMixin<ElementType> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
LWWSetMixin(@JsonProperty("data") Map<ElementType, LwwSet.Timestamps> struct) { }
|
LWWSetMixin(@JsonProperty("data") Map<ElementType, LwwSet.Timestamps> struct) {}
|
||||||
@JsonProperty("data") abstract Map<ElementType, LwwSet.Timestamps> getStruct();
|
|
||||||
|
@JsonProperty("data")
|
||||||
|
abstract Map<ElementType, LwwSet.Timestamps> getStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class LWWSetTimestampsMixin {
|
abstract class LWWSetTimestampsMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
LWWSetTimestampsMixin(@JsonProperty("add") long latestAdd, @JsonProperty("remove") long latestRemove) { }
|
LWWSetTimestampsMixin(
|
||||||
@JsonProperty("add") abstract long getLatestAdd();
|
@JsonProperty("add") long latestAdd, @JsonProperty("remove") long latestRemove) {}
|
||||||
@JsonProperty("remove") abstract long getLatestRemove();
|
|
||||||
|
@JsonProperty("add")
|
||||||
|
abstract long getLatestAdd();
|
||||||
|
|
||||||
|
@JsonProperty("remove")
|
||||||
|
abstract long getLatestRemove();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MaxChangeSetMixin<E> {
|
abstract class MaxChangeSetMixin<E> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
MaxChangeSetMixin(@JsonProperty("data") Map<E, Integer> struct) { }
|
MaxChangeSetMixin(@JsonProperty("data") Map<E, Integer> struct) {}
|
||||||
@JsonProperty("data") abstract Map<E, Integer> getStruct();
|
|
||||||
|
@JsonProperty("data")
|
||||||
|
abstract Map<E, Integer> getStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class TwoPhaseSetMixin<E> {
|
abstract class TwoPhaseSetMixin<E> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
TwoPhaseSetMixin(@JsonProperty("added") Set<E> added, @JsonProperty("removed") Set<E> removed) { }
|
TwoPhaseSetMixin(@JsonProperty("added") Set<E> added, @JsonProperty("removed") Set<E> removed) {}
|
||||||
@JsonProperty("added") abstract Set<E> getAdded();
|
|
||||||
@JsonProperty("removed") abstract Set<E> getRemoved();
|
@JsonProperty("added")
|
||||||
|
abstract Set<E> getAdded();
|
||||||
|
|
||||||
|
@JsonProperty("removed")
|
||||||
|
abstract Set<E> getRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GrowOnlySetMixin<E>{
|
abstract class GrowOnlySetMixin<E> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
GrowOnlySetMixin(@JsonProperty("elements") Set<E> elements){ }
|
GrowOnlySetMixin(@JsonProperty("elements") Set<E> elements) {}
|
||||||
@JsonProperty("elements") abstract Set<E> getElements();
|
|
||||||
@JsonIgnore abstract boolean isEmpty();
|
@JsonProperty("elements")
|
||||||
|
abstract Set<E> getElements();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
abstract boolean isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GrowOnlyCounterMixin {
|
abstract class GrowOnlyCounterMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
GrowOnlyCounterMixin(@JsonProperty("counters") Map<String, Long> counters) { }
|
GrowOnlyCounterMixin(@JsonProperty("counters") Map<String, Long> counters) {}
|
||||||
@JsonProperty("counters") abstract Map<String, Long> getCounters();
|
|
||||||
|
@JsonProperty("counters")
|
||||||
|
abstract Map<String, Long> getCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class PNCounterMixin {
|
abstract class PNCounterMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
PNCounterMixin(@JsonProperty("p-counters") Map<String, Long> up, @JsonProperty("n-counters") Map<String,Long> down) { }
|
PNCounterMixin(
|
||||||
@JsonProperty("p-counters") abstract Map<String, Long> getPCounters();
|
@JsonProperty("p-counters") Map<String, Long> up,
|
||||||
@JsonProperty("n-counters") abstract Map<String, Long> getNCounters();
|
@JsonProperty("n-counters") Map<String, Long> down) {}
|
||||||
|
|
||||||
|
@JsonProperty("p-counters")
|
||||||
|
abstract Map<String, Long> getPCounters();
|
||||||
|
|
||||||
|
@JsonProperty("n-counters")
|
||||||
|
abstract Map<String, Long> getNCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonTypeInfo(
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
use = JsonTypeInfo.Id.CLASS,
|
abstract class ReplicableMixin {}
|
||||||
include = JsonTypeInfo.As.PROPERTY,
|
|
||||||
property = "type")
|
|
||||||
abstract class ReplicableMixin {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class WhiteListReplicableMixin {
|
abstract class WhiteListReplicableMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
WhiteListReplicableMixin(@JsonProperty("whiteListMembers") List<LocalMember> whiteListMembers) { }
|
WhiteListReplicableMixin(@JsonProperty("whiteListMembers") List<LocalMember> whiteListMembers) {}
|
||||||
@JsonProperty("whiteListMembers") abstract List<LocalMember> getWhiteListMembers();
|
|
||||||
|
@JsonProperty("whiteListMembers")
|
||||||
|
abstract List<LocalMember> getWhiteListMembers();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BlackListReplicableMixin {
|
abstract class BlackListReplicableMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
BlackListReplicableMixin(@JsonProperty("blackListMembers") List<LocalMember> blackListMembers) { }
|
BlackListReplicableMixin(@JsonProperty("blackListMembers") List<LocalMember> blackListMembers) {}
|
||||||
@JsonProperty("blackListMembers") abstract List<LocalMember> getBlackListMembers();
|
|
||||||
|
@JsonProperty("blackListMembers")
|
||||||
|
abstract List<LocalMember> getBlackListMembers();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class VoteCandidateMixin {
|
abstract class VoteCandidateMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
VoteCandidateMixin(
|
VoteCandidateMixin(
|
||||||
@JsonProperty("candidateNodeId") String candidateNodeId,
|
@JsonProperty("candidateNodeId") String candidateNodeId,
|
||||||
@JsonProperty("votingKey") String votingKey,
|
@JsonProperty("votingKey") String votingKey,
|
||||||
@JsonProperty("votes") Map<String, Vote> votes
|
@JsonProperty("votes") Map<String, Vote> votes) {}
|
||||||
) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class VoteMixin {
|
abstract class VoteMixin {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
VoteMixin(
|
VoteMixin(
|
||||||
@JsonProperty("votingNode") String votingNode,
|
@JsonProperty("votingNode") String votingNode,
|
||||||
@JsonProperty("voteValue") Boolean voteValue,
|
@JsonProperty("voteValue") Boolean voteValue,
|
||||||
@JsonProperty("voteExchange") Boolean voteExchange,
|
@JsonProperty("voteExchange") Boolean voteExchange,
|
||||||
@JsonProperty("liveMembers") List<String> liveMembers,
|
@JsonProperty("liveMembers") List<String> liveMembers,
|
||||||
@JsonProperty("deadMembers") List<String> deadMembers
|
@JsonProperty("deadMembers") List<String> deadMembers) {}
|
||||||
) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MajorityVoteMixin<E>{
|
abstract class MajorityVoteMixin<E> {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
MajorityVoteMixin(@JsonProperty("voteCandidates") Map<String, VoteCandidate> voteCandidateMap){ }
|
MajorityVoteMixin(@JsonProperty("voteCandidates") Map<String, VoteCandidate> voteCandidateMap) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
//If anyone wants to take a stab at this. please have at it
|
// If anyone wants to take a stab at this. please have at it
|
||||||
//https://github.com/FasterXML/jackson-datatype-guava/blob/master/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java
|
// https://github.com/FasterXML/jackson-datatype-guava/blob/master/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java
|
||||||
public class CrdtModule extends SimpleModule {
|
public class CrdtModule extends SimpleModule {
|
||||||
|
|
||||||
private static final long serialVersionUID = 6134836523275023418L;
|
private static final long serialVersionUID = 6134836523275023418L;
|
||||||
@ -161,6 +191,4 @@ public class CrdtModule extends SimpleModule {
|
|||||||
context.setMixInAnnotations(VoteCandidate.class, VoteCandidateMixin.class);
|
context.setMixInAnnotations(VoteCandidate.class, VoteCandidateMixin.class);
|
||||||
context.setMixInAnnotations(Vote.class, VoteMixin.class);
|
context.setMixInAnnotations(Vote.class, VoteMixin.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +23,13 @@ import java.util.Map;
|
|||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
|
|
||||||
public class GrowOnlyCounter implements CrdtCounter<Long, GrowOnlyCounter> {
|
public class GrowOnlyCounter implements CrdtCounter<Long, GrowOnlyCounter> {
|
||||||
|
|
||||||
private final Map<String, Long> counters = new HashMap<>();
|
private final Map<String, Long> counters = new HashMap<>();
|
||||||
|
|
||||||
GrowOnlyCounter(Map<String, Long> counters) {
|
GrowOnlyCounter(Map<String, Long> counters) {
|
||||||
this.counters.putAll(counters);
|
this.counters.putAll(counters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrowOnlyCounter(GrowOnlyCounter growOnlyCounter, Builder builder) {
|
public GrowOnlyCounter(GrowOnlyCounter growOnlyCounter, Builder builder) {
|
||||||
counters.putAll(growOnlyCounter.counters);
|
counters.putAll(growOnlyCounter.counters);
|
||||||
if (counters.containsKey(builder.myId)) {
|
if (counters.containsKey(builder.myId)) {
|
||||||
@ -39,21 +39,21 @@ public class GrowOnlyCounter implements CrdtCounter<Long, GrowOnlyCounter> {
|
|||||||
counters.put(builder.myId, builder.counter);
|
counters.put(builder.myId, builder.counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrowOnlyCounter(Builder builder) {
|
public GrowOnlyCounter(Builder builder) {
|
||||||
counters.put(builder.myId, builder.counter);
|
counters.put(builder.myId, builder.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrowOnlyCounter(GossipManager manager) {
|
public GrowOnlyCounter(GossipManager manager) {
|
||||||
counters.put(manager.getMyself().getId(), 0L);
|
counters.put(manager.getMyself().getId(), 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrowOnlyCounter(GrowOnlyCounter growOnlyCounter, GrowOnlyCounter other) {
|
public GrowOnlyCounter(GrowOnlyCounter growOnlyCounter, GrowOnlyCounter other) {
|
||||||
counters.putAll(growOnlyCounter.counters);
|
counters.putAll(growOnlyCounter.counters);
|
||||||
for (Map.Entry<String, Long> entry : other.counters.entrySet()) {
|
for (Map.Entry<String, Long> entry : other.counters.entrySet()) {
|
||||||
String otherKey = entry.getKey();
|
String otherKey = entry.getKey();
|
||||||
Long otherValue = entry.getValue();
|
Long otherValue = entry.getValue();
|
||||||
|
|
||||||
if (counters.containsKey(otherKey)) {
|
if (counters.containsKey(otherKey)) {
|
||||||
Long newValue = Math.max(counters.get(otherKey), otherValue);
|
Long newValue = Math.max(counters.get(otherKey), otherValue);
|
||||||
counters.replace(otherKey, newValue);
|
counters.replace(otherKey, newValue);
|
||||||
@ -62,12 +62,12 @@ public class GrowOnlyCounter implements CrdtCounter<Long, GrowOnlyCounter> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GrowOnlyCounter merge(GrowOnlyCounter other) {
|
public GrowOnlyCounter merge(GrowOnlyCounter other) {
|
||||||
return new GrowOnlyCounter(this, other);
|
return new GrowOnlyCounter(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long value() {
|
public Long value() {
|
||||||
Long globalCount = 0L;
|
Long globalCount = 0L;
|
||||||
@ -76,40 +76,39 @@ public class GrowOnlyCounter implements CrdtCounter<Long, GrowOnlyCounter> {
|
|||||||
}
|
}
|
||||||
return globalCount;
|
return globalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GrowOnlyCounter optimize() {
|
public GrowOnlyCounter optimize() {
|
||||||
return new GrowOnlyCounter(counters);
|
return new GrowOnlyCounter(counters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass()) return false;
|
||||||
return false;
|
|
||||||
GrowOnlyCounter other = (GrowOnlyCounter) obj;
|
GrowOnlyCounter other = (GrowOnlyCounter) obj;
|
||||||
return value().longValue() == other.value().longValue();
|
return value().longValue() == other.value().longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GrowOnlyCounter [counters= " + counters + ", Value=" + value() + "]";
|
return "GrowOnlyCounter [counters= " + counters + ", Value=" + value() + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Long> getCounters() {
|
Map<String, Long> getCounters() {
|
||||||
return counters;
|
return counters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private final String myId;
|
private final String myId;
|
||||||
|
|
||||||
private Long counter;
|
private Long counter;
|
||||||
|
|
||||||
public Builder(GossipManager gossipManager) {
|
public Builder(GossipManager gossipManager) {
|
||||||
myId = gossipManager.getMyself().getId();
|
myId = gossipManager.getMyself().getId();
|
||||||
counter = 0L;
|
counter = 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrowOnlyCounter.Builder increment(Long count) {
|
public GrowOnlyCounter.Builder increment(Long count) {
|
||||||
counter += count;
|
counter += count;
|
||||||
return this;
|
return this;
|
||||||
|
@ -43,57 +43,60 @@ import org.apache.gossip.manager.SystemClock;
|
|||||||
DataTest - integration test with 2 nodes, LWWSet was serialized/deserialized, sent between nodes, merged
|
DataTest - integration test with 2 nodes, LWWSet was serialized/deserialized, sent between nodes, merged
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class LwwSet<ElementType> implements CrdtAddRemoveSet<ElementType, Set<ElementType>, LwwSet<ElementType>> {
|
public class LwwSet<ElementType>
|
||||||
static private Clock clock = new SystemClock();
|
implements CrdtAddRemoveSet<ElementType, Set<ElementType>, LwwSet<ElementType>> {
|
||||||
|
private static Clock clock = new SystemClock();
|
||||||
|
|
||||||
private final Map<ElementType, Timestamps> struct;
|
private final Map<ElementType, Timestamps> struct;
|
||||||
|
|
||||||
public LwwSet(){
|
public LwwSet() {
|
||||||
struct = new HashMap<>();
|
struct = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public LwwSet(ElementType... elements){
|
public LwwSet(ElementType... elements) {
|
||||||
this(new HashSet<>(Arrays.asList(elements)));
|
this(new HashSet<>(Arrays.asList(elements)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LwwSet(Set<ElementType> set){
|
public LwwSet(Set<ElementType> set) {
|
||||||
struct = new HashMap<>();
|
struct = new HashMap<>();
|
||||||
for (ElementType e : set){
|
for (ElementType e : set) {
|
||||||
struct.put(e, new Timestamps().updateAdd());
|
struct.put(e, new Timestamps().updateAdd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LwwSet(LwwSet<ElementType> first, LwwSet<ElementType> second){
|
public LwwSet(LwwSet<ElementType> first, LwwSet<ElementType> second) {
|
||||||
Function<ElementType, Timestamps> timestampsFor = p -> {
|
Function<ElementType, Timestamps> timestampsFor =
|
||||||
Timestamps firstTs = first.struct.get(p);
|
p -> {
|
||||||
Timestamps secondTs = second.struct.get(p);
|
Timestamps firstTs = first.struct.get(p);
|
||||||
if (firstTs == null){
|
Timestamps secondTs = second.struct.get(p);
|
||||||
return secondTs;
|
if (firstTs == null) {
|
||||||
}
|
return secondTs;
|
||||||
return firstTs.merge(secondTs);
|
}
|
||||||
};
|
return firstTs.merge(secondTs);
|
||||||
struct = Stream.concat(first.struct.keySet().stream(), second.struct.keySet().stream())
|
};
|
||||||
.distinct().collect(Collectors.toMap(p -> p, timestampsFor));
|
struct =
|
||||||
|
Stream.concat(first.struct.keySet().stream(), second.struct.keySet().stream())
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toMap(p -> p, timestampsFor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// for serialization
|
// for serialization
|
||||||
LwwSet(Map<ElementType, Timestamps> struct){
|
LwwSet(Map<ElementType, Timestamps> struct) {
|
||||||
this.struct = struct;
|
this.struct = struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LwwSet<ElementType> add(ElementType e){
|
public LwwSet<ElementType> add(ElementType e) {
|
||||||
return this.merge(new LwwSet<>(e));
|
return this.merge(new LwwSet<>(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<ElementType, Timestamps> getStruct(){
|
Map<ElementType, Timestamps> getStruct() {
|
||||||
return struct;
|
return struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LwwSet<ElementType> remove(ElementType e){
|
public LwwSet<ElementType> remove(ElementType e) {
|
||||||
Timestamps eTimestamps = struct.get(e);
|
Timestamps eTimestamps = struct.get(e);
|
||||||
if (eTimestamps == null || !eTimestamps.isPresent()){
|
if (eTimestamps == null || !eTimestamps.isPresent()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
Map<ElementType, Timestamps> changeMap = new HashMap<>();
|
Map<ElementType, Timestamps> changeMap = new HashMap<>();
|
||||||
@ -102,12 +105,12 @@ public class LwwSet<ElementType> implements CrdtAddRemoveSet<ElementType, Set<El
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LwwSet<ElementType> merge(LwwSet<ElementType> other){
|
public LwwSet<ElementType> merge(LwwSet<ElementType> other) {
|
||||||
return new LwwSet<>(this, other);
|
return new LwwSet<>(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ElementType> value(){
|
public Set<ElementType> value() {
|
||||||
return struct.entrySet().stream()
|
return struct.entrySet().stream()
|
||||||
.filter(entry -> entry.getValue().isPresent())
|
.filter(entry -> entry.getValue().isPresent())
|
||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
@ -115,55 +118,57 @@ public class LwwSet<ElementType> implements CrdtAddRemoveSet<ElementType, Set<El
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LwwSet<ElementType> optimize(){
|
public LwwSet<ElementType> optimize() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj){
|
public boolean equals(Object obj) {
|
||||||
return this == obj || (obj != null && getClass() == obj.getClass() && value().equals(((LwwSet) obj).value()));
|
return this == obj
|
||||||
|
|| (obj != null && getClass() == obj.getClass() && value().equals(((LwwSet) obj).value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Timestamps {
|
static class Timestamps {
|
||||||
private final long latestAdd;
|
private final long latestAdd;
|
||||||
private final long latestRemove;
|
private final long latestRemove;
|
||||||
|
|
||||||
Timestamps(){
|
Timestamps() {
|
||||||
latestAdd = 0;
|
latestAdd = 0;
|
||||||
latestRemove = 0;
|
latestRemove = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamps(long add, long remove){
|
Timestamps(long add, long remove) {
|
||||||
latestAdd = add;
|
latestAdd = add;
|
||||||
latestRemove = remove;
|
latestRemove = remove;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getLatestAdd(){
|
long getLatestAdd() {
|
||||||
return latestAdd;
|
return latestAdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getLatestRemove(){
|
long getLatestRemove() {
|
||||||
return latestRemove;
|
return latestRemove;
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider element present when addTime >= removeTime, so we prefer add to remove
|
// consider element present when addTime >= removeTime, so we prefer add to remove
|
||||||
boolean isPresent(){
|
boolean isPresent() {
|
||||||
return latestAdd >= latestRemove;
|
return latestAdd >= latestRemove;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamps updateAdd(){
|
Timestamps updateAdd() {
|
||||||
return new Timestamps(clock.nanoTime(), latestRemove);
|
return new Timestamps(clock.nanoTime(), latestRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamps updateRemove(){
|
Timestamps updateRemove() {
|
||||||
return new Timestamps(latestAdd, clock.nanoTime());
|
return new Timestamps(latestAdd, clock.nanoTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamps merge(Timestamps other){
|
Timestamps merge(Timestamps other) {
|
||||||
if (other == null){
|
if (other == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new Timestamps(Math.max(latestAdd, other.latestAdd), Math.max(latestRemove, other.latestRemove));
|
return new Timestamps(
|
||||||
|
Math.max(latestAdd, other.latestAdd), Math.max(latestRemove, other.latestRemove));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,57 +23,39 @@ import java.util.function.BiConsumer;
|
|||||||
import org.apache.gossip.crdt.OrSet.Builder.Operation;
|
import org.apache.gossip.crdt.OrSet.Builder.Operation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A immutable set
|
* A immutable set
|
||||||
*/
|
*/
|
||||||
public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
||||||
|
|
||||||
private final Map<E, Set<UUID>> elements = new HashMap<>();
|
private final Map<E, Set<UUID>> elements = new HashMap<>();
|
||||||
private final Map<E, Set<UUID>> tombstones = new HashMap<>();
|
private final Map<E, Set<UUID>> tombstones = new HashMap<>();
|
||||||
private final transient Set<E> val;
|
private final transient Set<E> val;
|
||||||
|
|
||||||
public OrSet(){
|
public OrSet() {
|
||||||
val = computeValue();
|
val = computeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
OrSet(Map<E, Set<UUID>> elements, Map<E, Set<UUID>> tombstones){
|
OrSet(Map<E, Set<UUID>> elements, Map<E, Set<UUID>> tombstones) {
|
||||||
this.elements.putAll(elements);
|
this.elements.putAll(elements);
|
||||||
this.tombstones.putAll(tombstones);
|
this.tombstones.putAll(tombstones);
|
||||||
val = computeValue();
|
val = computeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public OrSet(E ... elements){
|
public OrSet(E... elements) {
|
||||||
this(new HashSet<>(Arrays.asList(elements)));
|
this(new HashSet<>(Arrays.asList(elements)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrSet(Set<E> elements) {
|
public OrSet(Set<E> elements) {
|
||||||
for (E e: elements){
|
for (E e : elements) {
|
||||||
internalAdd(e);
|
internalAdd(e);
|
||||||
}
|
}
|
||||||
val = computeValue();
|
val = computeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrSet(Builder<E>builder){
|
public OrSet(Builder<E> builder) {
|
||||||
for (Builder<E>.OrSetElement<E> e: builder.elements){
|
for (Builder<E>.OrSetElement<E> e : builder.elements) {
|
||||||
if (e.operation == Operation.ADD){
|
if (e.operation == Operation.ADD) {
|
||||||
internalAdd(e.element);
|
|
||||||
} else {
|
|
||||||
internalRemove(e.element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val = computeValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This constructor is the way to remove elements from an existing set
|
|
||||||
* @param set
|
|
||||||
* @param builder
|
|
||||||
*/
|
|
||||||
public OrSet(OrSet<E> set, Builder<E> builder){
|
|
||||||
elements.putAll(set.elements);
|
|
||||||
tombstones.putAll(set.tombstones);
|
|
||||||
for (Builder<E>.OrSetElement<E> e: builder.elements){
|
|
||||||
if (e.operation == Operation.ADD){
|
|
||||||
internalAdd(e.element);
|
internalAdd(e.element);
|
||||||
} else {
|
} else {
|
||||||
internalRemove(e.element);
|
internalRemove(e.element);
|
||||||
@ -82,12 +64,32 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
val = computeValue();
|
val = computeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrSet(OrSet<E> left, OrSet<E> right){
|
/**
|
||||||
BiConsumer<Map<E, Set<UUID>>, Map<E, Set<UUID>>> internalMerge = (items, other) -> {
|
* This constructor is the way to remove elements from an existing set
|
||||||
for (Entry<E, Set<UUID>> l : other.entrySet()){
|
*
|
||||||
internalSetMerge(items, l.getKey(), l.getValue());
|
* @param set
|
||||||
|
* @param builder
|
||||||
|
*/
|
||||||
|
public OrSet(OrSet<E> set, Builder<E> builder) {
|
||||||
|
elements.putAll(set.elements);
|
||||||
|
tombstones.putAll(set.tombstones);
|
||||||
|
for (Builder<E>.OrSetElement<E> e : builder.elements) {
|
||||||
|
if (e.operation == Operation.ADD) {
|
||||||
|
internalAdd(e.element);
|
||||||
|
} else {
|
||||||
|
internalRemove(e.element);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
val = computeValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrSet(OrSet<E> left, OrSet<E> right) {
|
||||||
|
BiConsumer<Map<E, Set<UUID>>, Map<E, Set<UUID>>> internalMerge =
|
||||||
|
(items, other) -> {
|
||||||
|
for (Entry<E, Set<UUID>> l : other.entrySet()) {
|
||||||
|
internalSetMerge(items, l.getKey(), l.getValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
internalMerge.accept(elements, left.elements);
|
internalMerge.accept(elements, left.elements);
|
||||||
internalMerge.accept(elements, right.elements);
|
internalMerge.accept(elements, right.elements);
|
||||||
@ -121,31 +123,31 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
return new OrSet<>(this, new Builder<E>().remove(e));
|
return new OrSet<>(this, new Builder<E>().remove(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrSet.Builder<E> builder(){
|
public OrSet.Builder<E> builder() {
|
||||||
return new OrSet.Builder<>();
|
return new OrSet.Builder<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrSet<E> merge(OrSet<E> other) {
|
public OrSet<E> merge(OrSet<E> other) {
|
||||||
return new OrSet<E>(this, other);
|
return new OrSet<E>(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalAdd(E element) {
|
private void internalAdd(E element) {
|
||||||
Set<UUID> toMerge = new HashSet<>();
|
Set<UUID> toMerge = new HashSet<>();
|
||||||
toMerge.add(UUID.randomUUID());
|
toMerge.add(UUID.randomUUID());
|
||||||
internalSetMerge(elements, element, toMerge);
|
internalSetMerge(elements, element, toMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalRemove(E element){
|
private void internalRemove(E element) {
|
||||||
internalSetMerge(tombstones, element, elements.get(element));
|
internalSetMerge(tombstones, element, elements.get(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Computes the live values by analyzing the elements and tombstones
|
* Computes the live values by analyzing the elements and tombstones
|
||||||
*/
|
*/
|
||||||
private Set<E> computeValue(){
|
private Set<E> computeValue() {
|
||||||
Set<E> values = new HashSet<>();
|
Set<E> values = new HashSet<>();
|
||||||
for (Entry<E, Set<UUID>> entry: elements.entrySet()){
|
for (Entry<E, Set<UUID>> entry : elements.entrySet()) {
|
||||||
Set<UUID> deleteIds = tombstones.get(entry.getKey());
|
Set<UUID> deleteIds = tombstones.get(entry.getKey());
|
||||||
// if not all tokens for current element are in tombstones
|
// if not all tokens for current element are in tombstones
|
||||||
if (deleteIds == null || !deleteIds.containsAll(entry.getValue())) {
|
if (deleteIds == null || !deleteIds.containsAll(entry.getValue())) {
|
||||||
@ -154,7 +156,7 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<E> value() {
|
public Set<E> value() {
|
||||||
return val;
|
return val;
|
||||||
@ -164,7 +166,7 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
public OrSet<E> optimize() {
|
public OrSet<E> optimize() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return value().size();
|
return value().size();
|
||||||
}
|
}
|
||||||
@ -195,7 +197,6 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
public E next() {
|
public E next() {
|
||||||
return managed.next();
|
return managed.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "OrSet [elements=" + elements + ", tombstones=" + tombstones + "]" ;
|
return "OrSet [elements=" + elements + ", tombstones=" + tombstones + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -242,19 +243,14 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) return true;
|
||||||
return true;
|
if (obj == null) return false;
|
||||||
if (obj == null)
|
if (getClass() != obj.getClass()) return false;
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
OrSet other = (OrSet) obj;
|
OrSet other = (OrSet) obj;
|
||||||
if (elements == null) {
|
if (elements == null) {
|
||||||
if (other.elements != null)
|
if (other.elements != null) return false;
|
||||||
return false;
|
} else if (!value().equals(other.value())) return false;
|
||||||
} else if (!value().equals(other.value()))
|
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +263,8 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder<E> {
|
public static class Builder<E> {
|
||||||
private List<OrSetElement<E>> elements = new ArrayList<>();;
|
private List<OrSetElement<E>> elements = new ArrayList<>();
|
||||||
|
;
|
||||||
|
|
||||||
public Builder<E> add(E element) {
|
public Builder<E> add(E element) {
|
||||||
elements.add(new OrSetElement<E>(element, Operation.ADD));
|
elements.add(new OrSetElement<E>(element, Operation.ADD));
|
||||||
@ -284,8 +281,9 @@ public class OrSet<E> implements CrdtAddRemoveSet<E, Set<E>, OrSet<E>> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Operation {
|
public static enum Operation {
|
||||||
ADD, REMOVE
|
ADD,
|
||||||
|
REMOVE
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OrSetElement<EL> {
|
private class OrSetElement<EL> {
|
||||||
@ -298,5 +296,4 @@ public static enum Operation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -74,8 +74,7 @@ public class PNCounter implements CrdtCounter<Long, PNCounter> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass()) return false;
|
||||||
return false;
|
|
||||||
PNCounter other = (PNCounter) obj;
|
PNCounter other = (PNCounter) obj;
|
||||||
return value().longValue() == other.value().longValue();
|
return value().longValue() == other.value().longValue();
|
||||||
}
|
}
|
||||||
@ -118,8 +117,8 @@ public class PNCounter implements CrdtCounter<Long, PNCounter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public org.apache.gossip.crdt.GrowOnlyCounter.Builder makeGrowOnlyCounterBuilder(long value) {
|
public org.apache.gossip.crdt.GrowOnlyCounter.Builder makeGrowOnlyCounterBuilder(long value) {
|
||||||
org.apache.gossip.crdt.GrowOnlyCounter.Builder ret = new org.apache.gossip.crdt.GrowOnlyCounter.Builder(
|
org.apache.gossip.crdt.GrowOnlyCounter.Builder ret =
|
||||||
myManager);
|
new org.apache.gossip.crdt.GrowOnlyCounter.Builder(myManager);
|
||||||
ret.increment(value);
|
ret.increment(value);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -134,5 +133,4 @@ public class PNCounter implements CrdtCounter<Long, PNCounter> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package org.apache.gossip.event.data;
|
|||||||
|
|
||||||
import com.codahale.metrics.Gauge;
|
import com.codahale.metrics.Gauge;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
@ -46,7 +46,6 @@ import org.apache.gossip.model.SharedDataMessage;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class LockManager {
|
public class LockManager {
|
||||||
|
|
||||||
|
|
||||||
// For MetricRegistry
|
// For MetricRegistry
|
||||||
public static final String LOCK_KEY_SET_SIZE = "gossip.lock.key_set_size";
|
public static final String LOCK_KEY_SET_SIZE = "gossip.lock.key_set_size";
|
||||||
public static final String LOCK_TIME = "gossip.lock.time";
|
public static final String LOCK_TIME = "gossip.lock.time";
|
||||||
@ -57,8 +56,10 @@ public class LockManager {
|
|||||||
private final Set<String> lockKeys;
|
private final Set<String> lockKeys;
|
||||||
private final Timer lockTimeMetric;
|
private final Timer lockTimeMetric;
|
||||||
|
|
||||||
public LockManager(GossipManager gossipManager, final LockManagerSettings lockManagerSettings,
|
public LockManager(
|
||||||
MetricRegistry metrics) {
|
GossipManager gossipManager,
|
||||||
|
final LockManagerSettings lockManagerSettings,
|
||||||
|
MetricRegistry metrics) {
|
||||||
this.gossipManager = gossipManager;
|
this.gossipManager = gossipManager;
|
||||||
this.lockSettings = lockManagerSettings;
|
this.lockSettings = lockManagerSettings;
|
||||||
this.numberOfNodes = new AtomicInteger(lockSettings.getNumberOfNodes());
|
this.numberOfNodes = new AtomicInteger(lockSettings.getNumberOfNodes());
|
||||||
@ -66,14 +67,15 @@ public class LockManager {
|
|||||||
metrics.register(LOCK_KEY_SET_SIZE, (Gauge<Integer>) lockKeys::size);
|
metrics.register(LOCK_KEY_SET_SIZE, (Gauge<Integer>) lockKeys::size);
|
||||||
lockTimeMetric = metrics.timer(LOCK_TIME);
|
lockTimeMetric = metrics.timer(LOCK_TIME);
|
||||||
// Register listener for lock keys
|
// Register listener for lock keys
|
||||||
gossipManager.registerSharedDataSubscriber((key, oldValue, newValue) -> {
|
gossipManager.registerSharedDataSubscriber(
|
||||||
if (key.contains("lock/")) {
|
(key, oldValue, newValue) -> {
|
||||||
lockKeys.add(key);
|
if (key.contains("lock/")) {
|
||||||
}
|
lockKeys.add(key);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
voteService = Executors.newScheduledThreadPool(2);
|
voteService = Executors.newScheduledThreadPool(2);
|
||||||
voteService.scheduleAtFixedRate(this::updateVotes, 0, lockSettings.getVoteUpdateInterval(),
|
voteService.scheduleAtFixedRate(
|
||||||
TimeUnit.MILLISECONDS);
|
this::updateVotes, 0, lockSettings.getVoteUpdateInterval(), TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acquireSharedDataLock(String key) throws VoteFailedException {
|
public void acquireSharedDataLock(String key) throws VoteFailedException {
|
||||||
@ -89,21 +91,30 @@ public class LockManager {
|
|||||||
final Map<String, VoteCandidate> voteCandidatesMap = majorityVoteResult.value();
|
final Map<String, VoteCandidate> voteCandidatesMap = majorityVoteResult.value();
|
||||||
final Map<String, Boolean> voteResultMap = new HashMap<>();
|
final Map<String, Boolean> voteResultMap = new HashMap<>();
|
||||||
// Store the vote result for each vote candidate nodes
|
// Store the vote result for each vote candidate nodes
|
||||||
voteCandidatesMap.forEach((candidateId, voteCandidate) -> voteResultMap
|
voteCandidatesMap.forEach(
|
||||||
.put(candidateId, isVoteSuccess(voteCandidate)));
|
(candidateId, voteCandidate) ->
|
||||||
|
voteResultMap.put(candidateId, isVoteSuccess(voteCandidate)));
|
||||||
|
|
||||||
long passedCandidates = voteResultMap.values().stream().filter(aBoolean -> aBoolean).count();
|
long passedCandidates = voteResultMap.values().stream().filter(aBoolean -> aBoolean).count();
|
||||||
String myNodeId = gossipManager.getMyself().getId();
|
String myNodeId = gossipManager.getMyself().getId();
|
||||||
log.debug("NodeId=" + myNodeId + ", VoteMap=" + voteResultMap + ", WinnerCount="
|
log.debug(
|
||||||
+ passedCandidates);
|
"NodeId="
|
||||||
|
+ myNodeId
|
||||||
|
+ ", VoteMap="
|
||||||
|
+ voteResultMap
|
||||||
|
+ ", WinnerCount="
|
||||||
|
+ passedCandidates);
|
||||||
// Check for possible dead lock when no candidates were won
|
// Check for possible dead lock when no candidates were won
|
||||||
if (passedCandidates == 0) {
|
if (passedCandidates == 0) {
|
||||||
if (isDeadLock(voteCandidatesMap)) {
|
if (isDeadLock(voteCandidatesMap)) {
|
||||||
deadlockDetectCount++;
|
deadlockDetectCount++;
|
||||||
// Testing for deadlock is not always correct, therefore test for continues deadlocks
|
// Testing for deadlock is not always correct, therefore test for continues deadlocks
|
||||||
if (deadlockDetectCount >= lockSettings.getDeadlockDetectionThreshold()) {
|
if (deadlockDetectCount >= lockSettings.getDeadlockDetectionThreshold()) {
|
||||||
log.debug("Deadlock detected from node " + myNodeId + ". VoteCandidatesMap="
|
log.debug(
|
||||||
+ voteCandidatesMap);
|
"Deadlock detected from node "
|
||||||
|
+ myNodeId
|
||||||
|
+ ". VoteCandidatesMap="
|
||||||
|
+ voteCandidatesMap);
|
||||||
preventDeadLock(voteCandidatesMap);
|
preventDeadLock(voteCandidatesMap);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -133,12 +144,17 @@ public class LockManager {
|
|||||||
|
|
||||||
// Generate Crdt lock message for voting
|
// Generate Crdt lock message for voting
|
||||||
private SharedDataMessage generateLockMessage(String key) {
|
private SharedDataMessage generateLockMessage(String key) {
|
||||||
VoteCandidate voteCandidate = new VoteCandidate(gossipManager.getMyself().getId(), key,
|
VoteCandidate voteCandidate =
|
||||||
new ConcurrentHashMap<>());
|
new VoteCandidate(gossipManager.getMyself().getId(), key, new ConcurrentHashMap<>());
|
||||||
voteCandidate.addVote(new Vote(gossipManager.getMyself().getId(), true, false,
|
voteCandidate.addVote(
|
||||||
|
new Vote(
|
||||||
|
gossipManager.getMyself().getId(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
gossipManager.getLiveMembers().stream().map(Member::getId).collect(Collectors.toList()),
|
gossipManager.getLiveMembers().stream().map(Member::getId).collect(Collectors.toList()),
|
||||||
gossipManager.getDeadMembers().stream().map(Member::getId)
|
gossipManager.getDeadMembers().stream()
|
||||||
.collect(Collectors.toList())));
|
.map(Member::getId)
|
||||||
|
.collect(Collectors.toList())));
|
||||||
Map<String, VoteCandidate> voteCandidateMap = new ConcurrentHashMap<>();
|
Map<String, VoteCandidate> voteCandidateMap = new ConcurrentHashMap<>();
|
||||||
voteCandidateMap.put(voteCandidate.getCandidateNodeId(), voteCandidate);
|
voteCandidateMap.put(voteCandidate.getCandidateNodeId(), voteCandidate);
|
||||||
MajorityVote majorityVote = new MajorityVote(voteCandidateMap);
|
MajorityVote majorityVote = new MajorityVote(voteCandidateMap);
|
||||||
@ -167,7 +183,8 @@ public class LockManager {
|
|||||||
String myVoteCandidate = getVotedCandidateNodeId(myNodeId, voteCandidateMap);
|
String myVoteCandidate = getVotedCandidateNodeId(myNodeId, voteCandidateMap);
|
||||||
|
|
||||||
if (myVoteCandidate == null) {
|
if (myVoteCandidate == null) {
|
||||||
myVoteCandidate = lockSettings.getVoteSelector().getVoteCandidateId(voteCandidateMap.keySet());
|
myVoteCandidate =
|
||||||
|
lockSettings.getVoteSelector().getVoteCandidateId(voteCandidateMap.keySet());
|
||||||
}
|
}
|
||||||
for (VoteCandidate voteCandidate : voteCandidateMap.values()) {
|
for (VoteCandidate voteCandidate : voteCandidateMap.values()) {
|
||||||
if (voteCandidate.getCandidateNodeId().equals(myNodeId)) {
|
if (voteCandidate.getCandidateNodeId().equals(myNodeId)) {
|
||||||
@ -175,11 +192,17 @@ public class LockManager {
|
|||||||
}
|
}
|
||||||
// Vote for selected candidate
|
// Vote for selected candidate
|
||||||
boolean voteResult = voteCandidate.getCandidateNodeId().equals(myVoteCandidate);
|
boolean voteResult = voteCandidate.getCandidateNodeId().equals(myVoteCandidate);
|
||||||
voteCandidate.addVote(new Vote(gossipManager.getMyself().getId(), voteResult, false,
|
voteCandidate.addVote(
|
||||||
gossipManager.getLiveMembers().stream().map(Member::getId)
|
new Vote(
|
||||||
.collect(Collectors.toList()),
|
gossipManager.getMyself().getId(),
|
||||||
gossipManager.getDeadMembers().stream().map(Member::getId)
|
voteResult,
|
||||||
.collect(Collectors.toList())));
|
false,
|
||||||
|
gossipManager.getLiveMembers().stream()
|
||||||
|
.map(Member::getId)
|
||||||
|
.collect(Collectors.toList()),
|
||||||
|
gossipManager.getDeadMembers().stream()
|
||||||
|
.map(Member::getId)
|
||||||
|
.collect(Collectors.toList())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,9 +226,13 @@ public class LockManager {
|
|||||||
numberOfLiveNodes = numberOfNodes.get();
|
numberOfLiveNodes = numberOfNodes.get();
|
||||||
} else {
|
} else {
|
||||||
// numberOfNodes is not set by the user, therefore calculate it.
|
// numberOfNodes is not set by the user, therefore calculate it.
|
||||||
Set<String> liveNodes = voteCandidates.values().stream()
|
Set<String> liveNodes =
|
||||||
.map(voteCandidate -> voteCandidate.getVotes().values()).flatMap(Collection::stream)
|
voteCandidates.values().stream()
|
||||||
.map(Vote::getLiveMembers).flatMap(List::stream).collect(Collectors.toSet());
|
.map(voteCandidate -> voteCandidate.getVotes().values())
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(Vote::getLiveMembers)
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
numberOfLiveNodes = liveNodes.size();
|
numberOfLiveNodes = liveNodes.size();
|
||||||
}
|
}
|
||||||
for (VoteCandidate voteCandidate : voteCandidates.values()) {
|
for (VoteCandidate voteCandidate : voteCandidates.values()) {
|
||||||
@ -222,15 +249,17 @@ public class LockManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Set of nodes that is going to receive this nodes votes
|
// Set of nodes that is going to receive this nodes votes
|
||||||
List<String> donateCandidateIds = voteCandidates.keySet().stream()
|
List<String> donateCandidateIds =
|
||||||
.filter(s -> s.compareTo(myNodeId) < 0).collect(Collectors.toList());
|
voteCandidates.keySet().stream()
|
||||||
|
.filter(s -> s.compareTo(myNodeId) < 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
if (donateCandidateIds.size() == 0) {
|
if (donateCandidateIds.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Select a random node to donate
|
// Select a random node to donate
|
||||||
Random randomizer = new Random();
|
Random randomizer = new Random();
|
||||||
String selectedCandidateId = donateCandidateIds
|
String selectedCandidateId =
|
||||||
.get(randomizer.nextInt(donateCandidateIds.size()));
|
donateCandidateIds.get(randomizer.nextInt(donateCandidateIds.size()));
|
||||||
VoteCandidate selectedCandidate = voteCandidates.get(selectedCandidateId);
|
VoteCandidate selectedCandidate = voteCandidates.get(selectedCandidateId);
|
||||||
|
|
||||||
Set<Vote> myVotes = new HashSet<>(myResults.getVotes().values());
|
Set<Vote> myVotes = new HashSet<>(myResults.getVotes().values());
|
||||||
@ -246,11 +275,11 @@ public class LockManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.debug("Node " + myNodeId + " give up votes to node " + selectedCandidateId);
|
log.debug("Node " + myNodeId + " give up votes to node " + selectedCandidateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getVotedCandidateNodeId(String nodeId,
|
private String getVotedCandidateNodeId(
|
||||||
final Map<String, VoteCandidate> voteCandidates) {
|
String nodeId, final Map<String, VoteCandidate> voteCandidates) {
|
||||||
for (VoteCandidate voteCandidate : voteCandidates.values()) {
|
for (VoteCandidate voteCandidate : voteCandidates.values()) {
|
||||||
Vote vote = voteCandidate.getVotes().get(nodeId);
|
Vote vote = voteCandidate.getVotes().get(nodeId);
|
||||||
if (vote != null && vote.getVoteValue()) {
|
if (vote != null && vote.getVoteValue()) {
|
||||||
@ -279,15 +308,17 @@ public class LockManager {
|
|||||||
return numberOfLiveNodes > 0 && voteCount >= (numberOfLiveNodes / 2 + 1);
|
return numberOfLiveNodes > 0 && voteCount >= (numberOfLiveNodes / 2 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateLockKey(String key){
|
private String generateLockKey(String key) {
|
||||||
return "lock/" + key;
|
return "lock/" + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown(){
|
public void shutdown() {
|
||||||
voteService.shutdown();
|
voteService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the voted node id from this node for a given key
|
* Get the voted node id from this node for a given key
|
||||||
|
*
|
||||||
* @param key key of the data object
|
* @param key key of the data object
|
||||||
* @return Voted node id
|
* @return Voted node id
|
||||||
*/
|
*/
|
||||||
@ -302,10 +333,10 @@ public class LockManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the number of live nodes. If this value is negative, live nodes will be calculated
|
* Set the number of live nodes. If this value is negative, live nodes will be calculated
|
||||||
|
*
|
||||||
* @param numberOfNodes live node count or negative to calculate.
|
* @param numberOfNodes live node count or negative to calculate.
|
||||||
*/
|
*/
|
||||||
public void setNumberOfNodes(int numberOfNodes) {
|
public void setNumberOfNodes(int numberOfNodes) {
|
||||||
this.numberOfNodes.set(numberOfNodes);
|
this.numberOfNodes.set(numberOfNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.apache.gossip.crdt.Crdt;
|
import org.apache.gossip.crdt.Crdt;
|
||||||
|
|
||||||
/**
|
/** CRDT which used for distribute a votes for a given key. */
|
||||||
* CRDT which used for distribute a votes for a given key.
|
|
||||||
*/
|
|
||||||
public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVote> {
|
public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVote> {
|
||||||
|
|
||||||
private final Map<String, VoteCandidate> voteCandidates = new ConcurrentHashMap<>();
|
private final Map<String, VoteCandidate> voteCandidates = new ConcurrentHashMap<>();
|
||||||
@ -53,11 +51,13 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
}
|
}
|
||||||
// Merge votes for the same candidate
|
// Merge votes for the same candidate
|
||||||
for (String sameCandidateId : sameCandidatesSet) {
|
for (String sameCandidateId : sameCandidatesSet) {
|
||||||
if (this.voteCandidates.containsKey(sameCandidateId) && other.voteCandidates
|
if (this.voteCandidates.containsKey(sameCandidateId)
|
||||||
.containsKey(sameCandidateId)) {
|
&& other.voteCandidates.containsKey(sameCandidateId)) {
|
||||||
mergedCandidates.put(sameCandidateId,
|
mergedCandidates.put(
|
||||||
mergeCandidate(this.voteCandidates.get(sameCandidateId),
|
sameCandidateId,
|
||||||
other.voteCandidates.get(sameCandidateId)));
|
mergeCandidate(
|
||||||
|
this.voteCandidates.get(sameCandidateId),
|
||||||
|
other.voteCandidates.get(sameCandidateId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,10 +65,13 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge different votes for same candidate
|
// Merge different votes for same candidate
|
||||||
private VoteCandidate mergeCandidate(VoteCandidate firstCandidate,
|
private VoteCandidate mergeCandidate(
|
||||||
VoteCandidate secondCandidate) {
|
VoteCandidate firstCandidate, VoteCandidate secondCandidate) {
|
||||||
VoteCandidate mergeResult = new VoteCandidate(firstCandidate.getCandidateNodeId(),
|
VoteCandidate mergeResult =
|
||||||
firstCandidate.getVotingKey(), new ConcurrentHashMap<>());
|
new VoteCandidate(
|
||||||
|
firstCandidate.getCandidateNodeId(),
|
||||||
|
firstCandidate.getVotingKey(),
|
||||||
|
new ConcurrentHashMap<>());
|
||||||
Set<String> firstKeySet = firstCandidate.getVotes().keySet();
|
Set<String> firstKeySet = firstCandidate.getVotes().keySet();
|
||||||
Set<String> secondKeySet = secondCandidate.getVotes().keySet();
|
Set<String> secondKeySet = secondCandidate.getVotes().keySet();
|
||||||
Set<String> sameVoteNodeSet = getIntersection(firstKeySet, secondKeySet);
|
Set<String> sameVoteNodeSet = getIntersection(firstKeySet, secondKeySet);
|
||||||
@ -76,20 +79,26 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
// Merge different voters by combining their votes
|
// Merge different voters by combining their votes
|
||||||
for (String differentCandidateId : differentVoteNodeSet) {
|
for (String differentCandidateId : differentVoteNodeSet) {
|
||||||
if (firstCandidate.getVotes().containsKey(differentCandidateId)) {
|
if (firstCandidate.getVotes().containsKey(differentCandidateId)) {
|
||||||
mergeResult.getVotes()
|
mergeResult
|
||||||
.put(differentCandidateId, firstCandidate.getVotes().get(differentCandidateId));
|
.getVotes()
|
||||||
|
.put(differentCandidateId, firstCandidate.getVotes().get(differentCandidateId));
|
||||||
} else if (secondCandidate.getVotes().containsKey(differentCandidateId)) {
|
} else if (secondCandidate.getVotes().containsKey(differentCandidateId)) {
|
||||||
mergeResult.getVotes()
|
mergeResult
|
||||||
.put(differentCandidateId, secondCandidate.getVotes().get(differentCandidateId));
|
.getVotes()
|
||||||
|
.put(differentCandidateId, secondCandidate.getVotes().get(differentCandidateId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Merge vote for same voter
|
// Merge vote for same voter
|
||||||
for (String sameVoteNodeId : sameVoteNodeSet) {
|
for (String sameVoteNodeId : sameVoteNodeSet) {
|
||||||
if (firstCandidate.getVotes().containsKey(sameVoteNodeId) && secondCandidate.getVotes()
|
if (firstCandidate.getVotes().containsKey(sameVoteNodeId)
|
||||||
.containsKey(sameVoteNodeId)) {
|
&& secondCandidate.getVotes().containsKey(sameVoteNodeId)) {
|
||||||
mergeResult.getVotes().put(sameVoteNodeId,
|
mergeResult
|
||||||
mergeVote(firstCandidate.getVotes().get(sameVoteNodeId),
|
.getVotes()
|
||||||
secondCandidate.getVotes().get(sameVoteNodeId)));
|
.put(
|
||||||
|
sameVoteNodeId,
|
||||||
|
mergeVote(
|
||||||
|
firstCandidate.getVotes().get(sameVoteNodeId),
|
||||||
|
secondCandidate.getVotes().get(sameVoteNodeId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +140,6 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
Map<String, VoteCandidate> copy = new ConcurrentHashMap<>();
|
Map<String, VoteCandidate> copy = new ConcurrentHashMap<>();
|
||||||
copy.putAll(voteCandidates);
|
copy.putAll(voteCandidates);
|
||||||
return Collections.unmodifiableMap(copy);
|
return Collections.unmodifiableMap(copy);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -141,12 +149,9 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null)
|
if (obj == null) return false;
|
||||||
return false;
|
if (obj == this) return true;
|
||||||
if (obj == this)
|
if (!(obj instanceof MajorityVote)) return false;
|
||||||
return true;
|
|
||||||
if (!(obj instanceof MajorityVote))
|
|
||||||
return false;
|
|
||||||
MajorityVote other = (MajorityVote) obj;
|
MajorityVote other = (MajorityVote) obj;
|
||||||
return Objects.equals(voteCandidates, other.voteCandidates);
|
return Objects.equals(voteCandidates, other.voteCandidates);
|
||||||
}
|
}
|
||||||
@ -164,5 +169,4 @@ public class MajorityVote implements Crdt<Map<String, VoteCandidate>, MajorityVo
|
|||||||
public Map<String, VoteCandidate> getVoteCandidates() {
|
public Map<String, VoteCandidate> getVoteCandidates() {
|
||||||
return new ConcurrentHashMap<>(voteCandidates);
|
return new ConcurrentHashMap<>(voteCandidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,7 @@ package org.apache.gossip.lock.vote;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/** Store a voter details. */
|
||||||
* Store a voter details.
|
|
||||||
*/
|
|
||||||
public class Vote {
|
public class Vote {
|
||||||
private final String votingNode;
|
private final String votingNode;
|
||||||
private final Boolean voteValue; // TODO: 7/16/17 weight?
|
private final Boolean voteValue; // TODO: 7/16/17 weight?
|
||||||
@ -29,8 +27,12 @@ public class Vote {
|
|||||||
private final List<String> deadMembers;
|
private final List<String> deadMembers;
|
||||||
private Boolean voteExchange;
|
private Boolean voteExchange;
|
||||||
|
|
||||||
public Vote(String votingNode, Boolean voteValue, Boolean voteExchange, List<String> liveMembers,
|
public Vote(
|
||||||
List<String> deadMembers) {
|
String votingNode,
|
||||||
|
Boolean voteValue,
|
||||||
|
Boolean voteExchange,
|
||||||
|
List<String> liveMembers,
|
||||||
|
List<String> deadMembers) {
|
||||||
this.votingNode = votingNode;
|
this.votingNode = votingNode;
|
||||||
this.voteValue = voteValue;
|
this.voteValue = voteValue;
|
||||||
this.voteExchange = voteExchange;
|
this.voteExchange = voteExchange;
|
||||||
@ -64,7 +66,13 @@ public class Vote {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "votingNode=" + votingNode + ", voteValue=" + voteValue + ", liveMembers=" + liveMembers
|
return "votingNode="
|
||||||
+ ", deadMembers= " + deadMembers;
|
+ votingNode
|
||||||
|
+ ", voteValue="
|
||||||
|
+ voteValue
|
||||||
|
+ ", liveMembers="
|
||||||
|
+ liveMembers
|
||||||
|
+ ", deadMembers= "
|
||||||
|
+ deadMembers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,8 @@ import org.apache.gossip.model.ShutdownMessage;
|
|||||||
import org.apache.gossip.udp.*;
|
import org.apache.gossip.udp.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ActiveGossipThread sends information. Pick a random partner and send the membership list to that partner
|
* The ActiveGossipThread sends information. Pick a random partner and send the membership list to
|
||||||
|
* that partner
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractActiveGossiper {
|
public abstract class AbstractActiveGossiper {
|
||||||
@ -51,26 +52,26 @@ public abstract class AbstractActiveGossiper {
|
|||||||
private final Random random;
|
private final Random random;
|
||||||
private final GossipSettings gossipSettings;
|
private final GossipSettings gossipSettings;
|
||||||
|
|
||||||
public AbstractActiveGossiper(GossipManager gossipManager, GossipCore gossipCore, MetricRegistry registry) {
|
public AbstractActiveGossiper(
|
||||||
|
GossipManager gossipManager, GossipCore gossipCore, MetricRegistry registry) {
|
||||||
this.gossipManager = gossipManager;
|
this.gossipManager = gossipManager;
|
||||||
this.gossipCore = gossipCore;
|
this.gossipCore = gossipCore;
|
||||||
sharedDataHistogram = registry.histogram(name(AbstractActiveGossiper.class, "sharedDataHistogram-time"));
|
sharedDataHistogram =
|
||||||
sendPerNodeDataHistogram = registry.histogram(name(AbstractActiveGossiper.class, "sendPerNodeDataHistogram-time"));
|
registry.histogram(name(AbstractActiveGossiper.class, "sharedDataHistogram-time"));
|
||||||
sendMembershipHistogram = registry.histogram(name(AbstractActiveGossiper.class, "sendMembershipHistogram-time"));
|
sendPerNodeDataHistogram =
|
||||||
|
registry.histogram(name(AbstractActiveGossiper.class, "sendPerNodeDataHistogram-time"));
|
||||||
|
sendMembershipHistogram =
|
||||||
|
registry.histogram(name(AbstractActiveGossiper.class, "sendMembershipHistogram-time"));
|
||||||
random = new Random();
|
random = new Random();
|
||||||
gossipSettings = gossipManager.getSettings();
|
gossipSettings = gossipManager.getSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {}
|
||||||
|
|
||||||
}
|
public void shutdown() {}
|
||||||
|
|
||||||
public void shutdown() {
|
public final void sendShutdownMessage(LocalMember me, LocalMember target) {
|
||||||
|
if (target == null) {
|
||||||
}
|
|
||||||
|
|
||||||
public final void sendShutdownMessage(LocalMember me, LocalMember target){
|
|
||||||
if (target == null){
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShutdownMessage m = new ShutdownMessage();
|
ShutdownMessage m = new ShutdownMessage();
|
||||||
@ -94,8 +95,11 @@ public abstract class AbstractActiveGossiper {
|
|||||||
|
|
||||||
/** Send shared data one entry at a time. */
|
/** Send shared data one entry at a time. */
|
||||||
private void sendSharedDataInternal(LocalMember me, LocalMember member) {
|
private void sendSharedDataInternal(LocalMember me, LocalMember member) {
|
||||||
for (Entry<String, SharedDataMessage> innerEntry : gossipCore.getSharedData().entrySet()){
|
for (Entry<String, SharedDataMessage> innerEntry : gossipCore.getSharedData().entrySet()) {
|
||||||
if (innerEntry.getValue().getReplicable() != null && !innerEntry.getValue().getReplicable()
|
if (innerEntry.getValue().getReplicable() != null
|
||||||
|
&& !innerEntry
|
||||||
|
.getValue()
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(me, member, innerEntry.getValue())) {
|
.shouldReplicate(me, member, innerEntry.getValue())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -113,7 +117,10 @@ public abstract class AbstractActiveGossiper {
|
|||||||
udpMessage.setUuid(UUID.randomUUID().toString());
|
udpMessage.setUuid(UUID.randomUUID().toString());
|
||||||
udpMessage.setUriFrom(me.getId());
|
udpMessage.setUriFrom(me.getId());
|
||||||
for (Entry<String, SharedDataMessage> innerEntry : gossipCore.getSharedData().entrySet()) {
|
for (Entry<String, SharedDataMessage> innerEntry : gossipCore.getSharedData().entrySet()) {
|
||||||
if (innerEntry.getValue().getReplicable() != null && !innerEntry.getValue().getReplicable()
|
if (innerEntry.getValue().getReplicable() != null
|
||||||
|
&& !innerEntry
|
||||||
|
.getValue()
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(me, member, innerEntry.getValue())) {
|
.shouldReplicate(me, member, innerEntry.getValue())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -141,8 +148,8 @@ public abstract class AbstractActiveGossiper {
|
|||||||
copy.setReplicable(original.getReplicable());
|
copy.setReplicable(original.getReplicable());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void sendPerNodeData(LocalMember me, LocalMember member){
|
public final void sendPerNodeData(LocalMember me, LocalMember member) {
|
||||||
if (member == null){
|
if (member == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -156,9 +163,13 @@ public abstract class AbstractActiveGossiper {
|
|||||||
|
|
||||||
/** Send per node data one entry at a time. */
|
/** Send per node data one entry at a time. */
|
||||||
private void sendPerNodeDataInternal(LocalMember me, LocalMember member) {
|
private void sendPerNodeDataInternal(LocalMember me, LocalMember member) {
|
||||||
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> entry : gossipCore.getPerNodeData().entrySet()){
|
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> entry :
|
||||||
for (Entry<String, PerNodeDataMessage> innerEntry : entry.getValue().entrySet()){
|
gossipCore.getPerNodeData().entrySet()) {
|
||||||
if (innerEntry.getValue().getReplicable() != null && !innerEntry.getValue().getReplicable()
|
for (Entry<String, PerNodeDataMessage> innerEntry : entry.getValue().entrySet()) {
|
||||||
|
if (innerEntry.getValue().getReplicable() != null
|
||||||
|
&& !innerEntry
|
||||||
|
.getValue()
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(me, member, innerEntry.getValue())) {
|
.shouldReplicate(me, member, innerEntry.getValue())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -169,17 +180,20 @@ public abstract class AbstractActiveGossiper {
|
|||||||
gossipCore.sendOneWay(message, member.getUri());
|
gossipCore.sendOneWay(message, member.getUri());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send per node data by batching together several entries. */
|
/** Send per node data by batching together several entries. */
|
||||||
private void sendPerNodeDataInBulkInternal(LocalMember me, LocalMember member) {
|
private void sendPerNodeDataInBulkInternal(LocalMember me, LocalMember member) {
|
||||||
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> entry : gossipCore.getPerNodeData().entrySet()){
|
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> entry :
|
||||||
|
gossipCore.getPerNodeData().entrySet()) {
|
||||||
UdpPerNodeDataBulkMessage udpMessage = new UdpPerNodeDataBulkMessage();
|
UdpPerNodeDataBulkMessage udpMessage = new UdpPerNodeDataBulkMessage();
|
||||||
udpMessage.setUuid(UUID.randomUUID().toString());
|
udpMessage.setUuid(UUID.randomUUID().toString());
|
||||||
udpMessage.setUriFrom(me.getId());
|
udpMessage.setUriFrom(me.getId());
|
||||||
for (Entry<String, PerNodeDataMessage> innerEntry : entry.getValue().entrySet()){
|
for (Entry<String, PerNodeDataMessage> innerEntry : entry.getValue().entrySet()) {
|
||||||
if (innerEntry.getValue().getReplicable() != null && !innerEntry.getValue().getReplicable()
|
if (innerEntry.getValue().getReplicable() != null
|
||||||
|
&& !innerEntry
|
||||||
|
.getValue()
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(me, member, innerEntry.getValue())) {
|
.shouldReplicate(me, member, innerEntry.getValue())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -208,11 +222,9 @@ public abstract class AbstractActiveGossiper {
|
|||||||
copy.setReplicable(original.getReplicable());
|
copy.setReplicable(original.getReplicable());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Performs the sending of the membership list, after we have incremented our own heartbeat. */
|
||||||
* Performs the sending of the membership list, after we have incremented our own heartbeat.
|
|
||||||
*/
|
|
||||||
protected void sendMembershipList(LocalMember me, LocalMember member) {
|
protected void sendMembershipList(LocalMember me, LocalMember member) {
|
||||||
if (member == null){
|
if (member == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -225,15 +237,15 @@ public abstract class AbstractActiveGossiper {
|
|||||||
message.getMembers().add(convert(other));
|
message.getMembers().add(convert(other));
|
||||||
}
|
}
|
||||||
Response r = gossipCore.send(message, member.getUri());
|
Response r = gossipCore.send(message, member.getUri());
|
||||||
if (r instanceof ActiveGossipOk){
|
if (r instanceof ActiveGossipOk) {
|
||||||
//maybe count metrics here
|
// maybe count metrics here
|
||||||
} else {
|
} else {
|
||||||
log.debug("Message " + message + " generated response " + r);
|
log.debug("Message " + message + " generated response " + r);
|
||||||
}
|
}
|
||||||
sendMembershipHistogram.update(System.currentTimeMillis() - startTime);
|
sendMembershipHistogram.update(System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Member convert(LocalMember member){
|
protected final Member convert(LocalMember member) {
|
||||||
Member gm = new Member();
|
Member gm = new Member();
|
||||||
gm.setCluster(member.getClusterName());
|
gm.setCluster(member.getClusterName());
|
||||||
gm.setHeartbeat(member.getHeartbeat());
|
gm.setHeartbeat(member.getHeartbeat());
|
||||||
@ -244,9 +256,7 @@ public abstract class AbstractActiveGossiper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @param memberList An immutable list
|
||||||
* @param memberList
|
|
||||||
* An immutable list
|
|
||||||
* @return The chosen LocalGossipMember to gossip with.
|
* @return The chosen LocalGossipMember to gossip with.
|
||||||
*/
|
*/
|
||||||
protected LocalMember selectPartner(List<LocalMember> memberList) {
|
protected LocalMember selectPartner(List<LocalMember> memberList) {
|
||||||
|
@ -37,48 +37,50 @@ public class DataReaper {
|
|||||||
private final GossipCore gossipCore;
|
private final GossipCore gossipCore;
|
||||||
private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
|
private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
public DataReaper(GossipCore gossipCore, Clock clock){
|
public DataReaper(GossipCore gossipCore, Clock clock) {
|
||||||
this.gossipCore = gossipCore;
|
this.gossipCore = gossipCore;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(){
|
public void init() {
|
||||||
Runnable reapPerNodeData = () -> {
|
Runnable reapPerNodeData =
|
||||||
runPerNodeOnce();
|
() -> {
|
||||||
runSharedOnce();
|
runPerNodeOnce();
|
||||||
};
|
runSharedOnce();
|
||||||
|
};
|
||||||
scheduledExecutor.scheduleAtFixedRate(reapPerNodeData, 0, 5, TimeUnit.SECONDS);
|
scheduledExecutor.scheduleAtFixedRate(reapPerNodeData, 0, 5, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runSharedOnce(){
|
void runSharedOnce() {
|
||||||
for (Entry<String, SharedDataMessage> entry : gossipCore.getSharedData().entrySet()){
|
for (Entry<String, SharedDataMessage> entry : gossipCore.getSharedData().entrySet()) {
|
||||||
if (entry.getValue().getExpireAt() < clock.currentTimeMillis()){
|
if (entry.getValue().getExpireAt() < clock.currentTimeMillis()) {
|
||||||
gossipCore.getSharedData().remove(entry.getKey(), entry.getValue());
|
gossipCore.getSharedData().remove(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void runPerNodeOnce(){
|
void runPerNodeOnce() {
|
||||||
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> node : gossipCore.getPerNodeData().entrySet()){
|
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> node :
|
||||||
|
gossipCore.getPerNodeData().entrySet()) {
|
||||||
reapData(node.getValue());
|
reapData(node.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reapData(ConcurrentHashMap<String, PerNodeDataMessage> concurrentHashMap){
|
void reapData(ConcurrentHashMap<String, PerNodeDataMessage> concurrentHashMap) {
|
||||||
for (Entry<String, PerNodeDataMessage> entry : concurrentHashMap.entrySet()){
|
for (Entry<String, PerNodeDataMessage> entry : concurrentHashMap.entrySet()) {
|
||||||
if (entry.getValue().getExpireAt() < clock.currentTimeMillis()){
|
if (entry.getValue().getExpireAt() < clock.currentTimeMillis()) {
|
||||||
concurrentHashMap.remove(entry.getKey(), entry.getValue());
|
concurrentHashMap.remove(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close(){
|
public void close() {
|
||||||
scheduledExecutor.shutdown();
|
scheduledExecutor.shutdown();
|
||||||
try {
|
try {
|
||||||
scheduledExecutor.awaitTermination(1, TimeUnit.SECONDS);
|
scheduledExecutor.awaitTermination(1, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.gossip.LocalMember;
|
import org.apache.gossip.LocalMember;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends gossip traffic at different rates to other racks and data-centers.
|
* Sends gossip traffic at different rates to other racks and data-centers. This implementation
|
||||||
* This implementation controls the rate at which gossip traffic is shared.
|
* controls the rate at which gossip traffic is shared. There are two constructs Datacenter and
|
||||||
* There are two constructs Datacenter and Rack. It is assumed that bandwidth and latency is higher
|
* Rack. It is assumed that bandwidth and latency is higher in the rack than in the the datacenter.
|
||||||
* in the rack than in the the datacenter. We can adjust the rate at which we send messages to each group.
|
* We can adjust the rate at which we send messages to each group.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DatacenterRackAwareActiveGossiper extends AbstractActiveGossiper {
|
public class DatacenterRackAwareActiveGossiper extends AbstractActiveGossiper {
|
||||||
@ -49,125 +48,166 @@ public class DatacenterRackAwareActiveGossiper extends AbstractActiveGossiper {
|
|||||||
private int randomDeadMemberSendIntervalMs = 250;
|
private int randomDeadMemberSendIntervalMs = 250;
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
private ThreadPoolExecutor threadService;
|
private ThreadPoolExecutor threadService;
|
||||||
|
|
||||||
public DatacenterRackAwareActiveGossiper(GossipManager gossipManager, GossipCore gossipCore,
|
public DatacenterRackAwareActiveGossiper(
|
||||||
MetricRegistry registry) {
|
GossipManager gossipManager, GossipCore gossipCore, MetricRegistry registry) {
|
||||||
super(gossipManager, gossipCore, registry);
|
super(gossipManager, gossipCore, registry);
|
||||||
scheduledExecutorService = Executors.newScheduledThreadPool(2);
|
scheduledExecutorService = Executors.newScheduledThreadPool(2);
|
||||||
workQueue = new ArrayBlockingQueue<Runnable>(1024);
|
workQueue = new ArrayBlockingQueue<Runnable>(1024);
|
||||||
threadService = new ThreadPoolExecutor(1, 30, 1, TimeUnit.SECONDS, workQueue,
|
threadService =
|
||||||
new ThreadPoolExecutor.DiscardOldestPolicy());
|
new ThreadPoolExecutor(
|
||||||
|
1, 30, 1, TimeUnit.SECONDS, workQueue, new ThreadPoolExecutor.DiscardOldestPolicy());
|
||||||
try {
|
try {
|
||||||
sameRackGossipIntervalMs = Integer.parseInt(gossipManager.getSettings()
|
sameRackGossipIntervalMs =
|
||||||
.getActiveGossipProperties().get("sameRackGossipIntervalMs"));
|
Integer.parseInt(
|
||||||
} catch (RuntimeException ex) { }
|
gossipManager
|
||||||
|
.getSettings()
|
||||||
|
.getActiveGossipProperties()
|
||||||
|
.get("sameRackGossipIntervalMs"));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
sameDcGossipIntervalMs = Integer.parseInt(gossipManager.getSettings()
|
sameDcGossipIntervalMs =
|
||||||
.getActiveGossipProperties().get("sameDcGossipIntervalMs"));
|
Integer.parseInt(
|
||||||
} catch (RuntimeException ex) { }
|
gossipManager
|
||||||
|
.getSettings()
|
||||||
|
.getActiveGossipProperties()
|
||||||
|
.get("sameDcGossipIntervalMs"));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
differentDatacenterGossipIntervalMs = Integer.parseInt(gossipManager.getSettings()
|
differentDatacenterGossipIntervalMs =
|
||||||
.getActiveGossipProperties().get("differentDatacenterGossipIntervalMs"));
|
Integer.parseInt(
|
||||||
} catch (RuntimeException ex) { }
|
gossipManager
|
||||||
|
.getSettings()
|
||||||
|
.getActiveGossipProperties()
|
||||||
|
.get("differentDatacenterGossipIntervalMs"));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
randomDeadMemberSendIntervalMs = Integer.parseInt(gossipManager.getSettings()
|
randomDeadMemberSendIntervalMs =
|
||||||
.getActiveGossipProperties().get("randomDeadMemberSendIntervalMs"));
|
Integer.parseInt(
|
||||||
} catch (RuntimeException ex) { }
|
gossipManager
|
||||||
|
.getSettings()
|
||||||
|
.getActiveGossipProperties()
|
||||||
|
.get("randomDeadMemberSendIntervalMs"));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
super.init();
|
super.init();
|
||||||
//same rack
|
// same rack
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
threadService.execute(() -> sendToSameRackMember()),
|
() -> threadService.execute(() -> sendToSameRackMember()),
|
||||||
0, sameRackGossipIntervalMs, TimeUnit.MILLISECONDS);
|
0,
|
||||||
|
sameRackGossipIntervalMs,
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
TimeUnit.MILLISECONDS);
|
||||||
threadService.execute(() -> sendToSameRackMemberPerNode()),
|
|
||||||
0, sameRackGossipIntervalMs, TimeUnit.MILLISECONDS);
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
|
() -> threadService.execute(() -> sendToSameRackMemberPerNode()),
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
0,
|
||||||
threadService.execute(() -> sendToSameRackShared()),
|
sameRackGossipIntervalMs,
|
||||||
0, sameRackGossipIntervalMs, TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
//same dc different rack
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
() -> threadService.execute(() -> sendToSameRackShared()),
|
||||||
threadService.execute(() -> sameDcDiffernetRackMember()),
|
0,
|
||||||
0, sameDcGossipIntervalMs, TimeUnit.MILLISECONDS);
|
sameRackGossipIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
|
||||||
threadService.execute(() -> sameDcDiffernetRackPerNode()),
|
// same dc different rack
|
||||||
0, sameDcGossipIntervalMs, TimeUnit.MILLISECONDS);
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
|
() -> threadService.execute(() -> sameDcDiffernetRackMember()),
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
0,
|
||||||
threadService.execute(() -> sameDcDiffernetRackShared()),
|
sameDcGossipIntervalMs,
|
||||||
0, sameDcGossipIntervalMs, TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
//different dc
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
() -> threadService.execute(() -> sameDcDiffernetRackPerNode()),
|
||||||
threadService.execute(() -> differentDcMember()),
|
0,
|
||||||
0, differentDatacenterGossipIntervalMs, TimeUnit.MILLISECONDS);
|
sameDcGossipIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
|
||||||
threadService.execute(() -> differentDcPerNode()),
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
0, differentDatacenterGossipIntervalMs, TimeUnit.MILLISECONDS);
|
() -> threadService.execute(() -> sameDcDiffernetRackShared()),
|
||||||
|
0,
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
sameDcGossipIntervalMs,
|
||||||
threadService.execute(() -> differentDcShared()),
|
TimeUnit.MILLISECONDS);
|
||||||
0, differentDatacenterGossipIntervalMs, TimeUnit.MILLISECONDS);
|
|
||||||
|
// different dc
|
||||||
//the dead
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
scheduledExecutorService.scheduleAtFixedRate(() ->
|
() -> threadService.execute(() -> differentDcMember()),
|
||||||
threadService.execute(() -> sendToDeadMember()),
|
0,
|
||||||
0, randomDeadMemberSendIntervalMs, TimeUnit.MILLISECONDS);
|
differentDatacenterGossipIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
|
() -> threadService.execute(() -> differentDcPerNode()),
|
||||||
|
0,
|
||||||
|
differentDatacenterGossipIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
|
() -> threadService.execute(() -> differentDcShared()),
|
||||||
|
0,
|
||||||
|
differentDatacenterGossipIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// the dead
|
||||||
|
scheduledExecutorService.scheduleAtFixedRate(
|
||||||
|
() -> threadService.execute(() -> sendToDeadMember()),
|
||||||
|
0,
|
||||||
|
randomDeadMemberSendIntervalMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendToDeadMember() {
|
private void sendToDeadMember() {
|
||||||
sendMembershipList(gossipManager.getMyself(), selectPartner(gossipManager.getDeadMembers()));
|
sendMembershipList(gossipManager.getMyself(), selectPartner(gossipManager.getDeadMembers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalMember> differentDataCenter(){
|
private List<LocalMember> differentDataCenter() {
|
||||||
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
||||||
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
||||||
if (myDc == null|| rack == null){
|
if (myDc == null || rack == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<LocalMember> notMyDc = new ArrayList<LocalMember>(10);
|
List<LocalMember> notMyDc = new ArrayList<LocalMember>(10);
|
||||||
for (LocalMember i : gossipManager.getLiveMembers()){
|
for (LocalMember i : gossipManager.getLiveMembers()) {
|
||||||
if (!myDc.equals(i.getProperties().get(DATACENTER))){
|
if (!myDc.equals(i.getProperties().get(DATACENTER))) {
|
||||||
notMyDc.add(i);
|
notMyDc.add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notMyDc;
|
return notMyDc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalMember> sameDatacenterDifferentRack(){
|
private List<LocalMember> sameDatacenterDifferentRack() {
|
||||||
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
||||||
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
||||||
if (myDc == null|| rack == null){
|
if (myDc == null || rack == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<LocalMember> notMyDc = new ArrayList<LocalMember>(10);
|
List<LocalMember> notMyDc = new ArrayList<LocalMember>(10);
|
||||||
for (LocalMember i : gossipManager.getLiveMembers()){
|
for (LocalMember i : gossipManager.getLiveMembers()) {
|
||||||
if (myDc.equals(i.getProperties().get(DATACENTER)) && !rack.equals(i.getProperties().get(RACK))){
|
if (myDc.equals(i.getProperties().get(DATACENTER))
|
||||||
|
&& !rack.equals(i.getProperties().get(RACK))) {
|
||||||
notMyDc.add(i);
|
notMyDc.add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notMyDc;
|
return notMyDc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalMember> sameRackNodes(){
|
private List<LocalMember> sameRackNodes() {
|
||||||
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
String myDc = gossipManager.getMyself().getProperties().get(DATACENTER);
|
||||||
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
String rack = gossipManager.getMyself().getProperties().get(RACK);
|
||||||
if (myDc == null|| rack == null){
|
if (myDc == null || rack == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<LocalMember> sameDcAndRack = new ArrayList<LocalMember>(10);
|
List<LocalMember> sameDcAndRack = new ArrayList<LocalMember>(10);
|
||||||
for (LocalMember i : gossipManager.getLiveMembers()){
|
for (LocalMember i : gossipManager.getLiveMembers()) {
|
||||||
if (myDc.equals(i.getProperties().get(DATACENTER))
|
if (myDc.equals(i.getProperties().get(DATACENTER))
|
||||||
&& rack.equals(i.getProperties().get(RACK))){
|
&& rack.equals(i.getProperties().get(RACK))) {
|
||||||
sameDcAndRack.add(i);
|
sameDcAndRack.add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,39 +218,39 @@ public class DatacenterRackAwareActiveGossiper extends AbstractActiveGossiper {
|
|||||||
LocalMember i = selectPartner(sameRackNodes());
|
LocalMember i = selectPartner(sameRackNodes());
|
||||||
sendMembershipList(gossipManager.getMyself(), i);
|
sendMembershipList(gossipManager.getMyself(), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendToSameRackMemberPerNode() {
|
private void sendToSameRackMemberPerNode() {
|
||||||
sendPerNodeData(gossipManager.getMyself(), selectPartner(sameRackNodes()));
|
sendPerNodeData(gossipManager.getMyself(), selectPartner(sameRackNodes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendToSameRackShared() {
|
private void sendToSameRackShared() {
|
||||||
sendSharedData(gossipManager.getMyself(), selectPartner(sameRackNodes()));
|
sendSharedData(gossipManager.getMyself(), selectPartner(sameRackNodes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void differentDcMember() {
|
private void differentDcMember() {
|
||||||
sendMembershipList(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
sendMembershipList(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void differentDcPerNode() {
|
private void differentDcPerNode() {
|
||||||
sendPerNodeData(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
sendPerNodeData(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void differentDcShared() {
|
private void differentDcShared() {
|
||||||
sendSharedData(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
sendSharedData(gossipManager.getMyself(), selectPartner(differentDataCenter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sameDcDiffernetRackMember() {
|
private void sameDcDiffernetRackMember() {
|
||||||
sendMembershipList(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
sendMembershipList(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sameDcDiffernetRackPerNode() {
|
private void sameDcDiffernetRackPerNode() {
|
||||||
sendPerNodeData(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
sendPerNodeData(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sameDcDiffernetRackShared() {
|
private void sameDcDiffernetRackShared() {
|
||||||
sendSharedData(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
sendSharedData(gossipManager.getMyself(), selectPartner(sameDatacenterDifferentRack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
super.shutdown();
|
super.shutdown();
|
||||||
@ -228,11 +268,9 @@ public class DatacenterRackAwareActiveGossiper extends AbstractActiveGossiper {
|
|||||||
log.debug("Issue during shutdown", e);
|
log.debug("Issue during shutdown", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** sends an optimistic shutdown message to several clusters nodes */
|
||||||
* sends an optimistic shutdown message to several clusters nodes
|
protected void sendShutdownMessage() {
|
||||||
*/
|
|
||||||
protected void sendShutdownMessage(){
|
|
||||||
List<LocalMember> l = gossipManager.getLiveMembers();
|
List<LocalMember> l = gossipManager.getLiveMembers();
|
||||||
int sendTo = l.size() < 3 ? 1 : l.size() / 3;
|
int sendTo = l.size() < 3 ? 1 : l.size() / 3;
|
||||||
for (int i = 0; i < sendTo; i++) {
|
for (int i = 0; i < sendTo; i++) {
|
||||||
|
@ -45,38 +45,41 @@ import org.apache.gossip.udp.Trackable;
|
|||||||
public class GossipCore implements GossipCoreConstants {
|
public class GossipCore implements GossipCoreConstants {
|
||||||
|
|
||||||
private final GossipManager gossipManager;
|
private final GossipManager gossipManager;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>> perNodeData;
|
private final ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>>
|
||||||
@Getter
|
perNodeData;
|
||||||
private final ConcurrentHashMap<String, SharedDataMessage> sharedData;
|
|
||||||
|
@Getter private final ConcurrentHashMap<String, SharedDataMessage> sharedData;
|
||||||
private final Meter messageSerdeException;
|
private final Meter messageSerdeException;
|
||||||
private final Meter transmissionException;
|
private final Meter transmissionException;
|
||||||
private final Meter transmissionSuccess;
|
private final Meter transmissionSuccess;
|
||||||
private final DataEventManager eventManager;
|
private final DataEventManager eventManager;
|
||||||
private ConcurrentHashMap<String, LatchAndBase> requests;
|
private final ConcurrentHashMap<String, LatchAndBase> requests;
|
||||||
public GossipCore(GossipManager manager, MetricRegistry metrics){
|
|
||||||
|
public GossipCore(GossipManager manager, MetricRegistry metrics) {
|
||||||
this.gossipManager = manager;
|
this.gossipManager = manager;
|
||||||
requests = new ConcurrentHashMap<>();
|
requests = new ConcurrentHashMap<>();
|
||||||
perNodeData = new ConcurrentHashMap<>();
|
perNodeData = new ConcurrentHashMap<>();
|
||||||
sharedData = new ConcurrentHashMap<>();
|
sharedData = new ConcurrentHashMap<>();
|
||||||
eventManager = new DataEventManager(metrics);
|
eventManager = new DataEventManager(metrics);
|
||||||
metrics.register(PER_NODE_DATA_SIZE, (Gauge<Integer>)() -> perNodeData.size());
|
metrics.register(PER_NODE_DATA_SIZE, (Gauge<Integer>) perNodeData::size);
|
||||||
metrics.register(SHARED_DATA_SIZE, (Gauge<Integer>)() -> sharedData.size());
|
metrics.register(SHARED_DATA_SIZE, (Gauge<Integer>) sharedData::size);
|
||||||
metrics.register(REQUEST_SIZE, (Gauge<Integer>)() -> requests.size());
|
metrics.register(REQUEST_SIZE, (Gauge<Integer>) requests::size);
|
||||||
messageSerdeException = metrics.meter(MESSAGE_SERDE_EXCEPTION);
|
messageSerdeException = metrics.meter(MESSAGE_SERDE_EXCEPTION);
|
||||||
transmissionException = metrics.meter(MESSAGE_TRANSMISSION_EXCEPTION);
|
transmissionException = metrics.meter(MESSAGE_TRANSMISSION_EXCEPTION);
|
||||||
transmissionSuccess = metrics.meter(MESSAGE_TRANSMISSION_SUCCESS);
|
transmissionSuccess = metrics.meter(MESSAGE_TRANSMISSION_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public void addSharedData(SharedDataMessage message) {
|
public void addSharedData(SharedDataMessage message) {
|
||||||
while (true){
|
while (true) {
|
||||||
SharedDataMessage previous = sharedData.putIfAbsent(message.getKey(), message);
|
SharedDataMessage previous = sharedData.putIfAbsent(message.getKey(), message);
|
||||||
if (previous == null){
|
if (previous == null) {
|
||||||
eventManager.notifySharedData(message.getKey(), message.getPayload(), null);
|
eventManager.notifySharedData(message.getKey(), message.getPayload(), null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.getPayload() instanceof Crdt){
|
if (message.getPayload() instanceof Crdt) {
|
||||||
SharedDataMessage merged = new SharedDataMessage();
|
SharedDataMessage merged = new SharedDataMessage();
|
||||||
merged.setExpireAt(message.getExpireAt());
|
merged.setExpireAt(message.getExpireAt());
|
||||||
merged.setKey(message.getKey());
|
merged.setKey(message.getKey());
|
||||||
@ -85,18 +88,19 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
Crdt mergedCrdt = ((Crdt) previous.getPayload()).merge((Crdt) message.getPayload());
|
Crdt mergedCrdt = ((Crdt) previous.getPayload()).merge((Crdt) message.getPayload());
|
||||||
merged.setPayload(mergedCrdt);
|
merged.setPayload(mergedCrdt);
|
||||||
boolean replaced = sharedData.replace(message.getKey(), previous, merged);
|
boolean replaced = sharedData.replace(message.getKey(), previous, merged);
|
||||||
if (replaced){
|
if (replaced) {
|
||||||
if(!merged.getPayload().equals(previous.getPayload())) {
|
if (!merged.getPayload().equals(previous.getPayload())) {
|
||||||
eventManager
|
eventManager.notifySharedData(
|
||||||
.notifySharedData(message.getKey(), merged.getPayload(), previous.getPayload());
|
message.getKey(), merged.getPayload(), previous.getPayload());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (previous.getTimestamp() < message.getTimestamp()){
|
if (previous.getTimestamp() < message.getTimestamp()) {
|
||||||
boolean result = sharedData.replace(message.getKey(), previous, message);
|
boolean result = sharedData.replace(message.getKey(), previous, message);
|
||||||
if (result){
|
if (result) {
|
||||||
eventManager.notifySharedData(message.getKey(), message.getPayload(), previous.getPayload());
|
eventManager.notifySharedData(
|
||||||
|
message.getKey(), message.getPayload(), previous.getPayload());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -106,29 +110,30 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPerNodeData(PerNodeDataMessage message){
|
public void addPerNodeData(PerNodeDataMessage message) {
|
||||||
ConcurrentHashMap<String,PerNodeDataMessage> nodeMap = new ConcurrentHashMap<>();
|
ConcurrentHashMap<String, PerNodeDataMessage> nodeMap = new ConcurrentHashMap<>();
|
||||||
nodeMap.put(message.getKey(), message);
|
nodeMap.put(message.getKey(), message);
|
||||||
nodeMap = perNodeData.putIfAbsent(message.getNodeId(), nodeMap);
|
nodeMap = perNodeData.putIfAbsent(message.getNodeId(), nodeMap);
|
||||||
if (nodeMap != null){
|
if (nodeMap != null) {
|
||||||
PerNodeDataMessage current = nodeMap.get(message.getKey());
|
PerNodeDataMessage current = nodeMap.get(message.getKey());
|
||||||
if (current == null){
|
if (current == null) {
|
||||||
nodeMap.putIfAbsent(message.getKey(), message);
|
nodeMap.putIfAbsent(message.getKey(), message);
|
||||||
eventManager.notifyPerNodeData(message.getNodeId(), message.getKey(), message.getPayload(), null);
|
eventManager.notifyPerNodeData(
|
||||||
|
message.getNodeId(), message.getKey(), message.getPayload(), null);
|
||||||
} else {
|
} else {
|
||||||
if (current.getTimestamp() < message.getTimestamp()){
|
if (current.getTimestamp() < message.getTimestamp()) {
|
||||||
nodeMap.replace(message.getKey(), current, message);
|
nodeMap.replace(message.getKey(), current, message);
|
||||||
eventManager.notifyPerNodeData(message.getNodeId(), message.getKey(), message.getPayload(),
|
eventManager.notifyPerNodeData(
|
||||||
current.getPayload());
|
message.getNodeId(), message.getKey(), message.getPayload(), current.getPayload());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eventManager.notifyPerNodeData(message.getNodeId(), message.getKey(), message.getPayload(), null);
|
eventManager.notifyPerNodeData(
|
||||||
|
message.getNodeId(), message.getKey(), message.getPayload(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown(){
|
public void shutdown() {}
|
||||||
}
|
|
||||||
|
|
||||||
public void receive(Base base) {
|
public void receive(Base base) {
|
||||||
if (!gossipManager.getMessageHandler().invoke(this, gossipManager, base)) {
|
if (!gossipManager.getMessageHandler().invoke(this, gossipManager, base)) {
|
||||||
@ -137,8 +142,8 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a blocking message.
|
* Sends a blocking message. todo: move functionality to TransportManager layer.
|
||||||
* todo: move functionality to TransportManager layer.
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* @param uri
|
* @param uri
|
||||||
* @throws RuntimeException if data can not be serialized or in transmission error
|
* @throws RuntimeException if data can not be serialized or in transmission error
|
||||||
@ -160,13 +165,13 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response send(Base message, URI uri){
|
public Response send(Base message, URI uri) {
|
||||||
log.debug("Sending " + message);
|
log.debug("Sending " + message);
|
||||||
log.debug("Current request queue " + requests);
|
log.debug("Current request queue " + requests);
|
||||||
|
|
||||||
final Trackable t;
|
final Trackable t;
|
||||||
LatchAndBase latchAndBase = null;
|
LatchAndBase latchAndBase = null;
|
||||||
if (message instanceof Trackable){
|
if (message instanceof Trackable) {
|
||||||
t = (Trackable) message;
|
t = (Trackable) message;
|
||||||
latchAndBase = new LatchAndBase();
|
latchAndBase = new LatchAndBase();
|
||||||
requests.put(t.getUuid() + "/" + t.getUriFrom(), latchAndBase);
|
requests.put(t.getUuid() + "/" + t.getUriFrom(), latchAndBase);
|
||||||
@ -174,27 +179,28 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
t = null;
|
t = null;
|
||||||
}
|
}
|
||||||
sendInternal(message, uri);
|
sendInternal(message, uri);
|
||||||
if (latchAndBase == null){
|
if (latchAndBase == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean complete = latchAndBase.latch.await(1, TimeUnit.SECONDS);
|
boolean complete = latchAndBase.latch.await(1, TimeUnit.SECONDS);
|
||||||
if (complete){
|
if (complete) {
|
||||||
return (Response) latchAndBase.base;
|
return (Response) latchAndBase.base;
|
||||||
} else{
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
requests.remove(t.getUuid() + "/" + t.getUriFrom());
|
requests.remove(t.getUuid() + "/" + t.getUriFrom());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a message across the network while blocking. Catches and ignores IOException in transmission. Used
|
* Sends a message across the network while blocking. Catches and ignores IOException in
|
||||||
* when the protocol for the message is not to wait for a response
|
* transmission. Used when the protocol for the message is not to wait for a response
|
||||||
|
*
|
||||||
* @param message the message to send
|
* @param message the message to send
|
||||||
* @param u the uri to send it to
|
* @param u the uri to send it to
|
||||||
*/
|
*/
|
||||||
@ -217,10 +223,9 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
*
|
*
|
||||||
* @param senderMember
|
* @param senderMember
|
||||||
* @param remoteList
|
* @param remoteList
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public void mergeLists(RemoteMember senderMember, List<Member> remoteList) {
|
public void mergeLists(RemoteMember senderMember, List<Member> remoteList) {
|
||||||
if (log.isDebugEnabled()){
|
if (log.isDebugEnabled()) {
|
||||||
debugState(senderMember, remoteList);
|
debugState(senderMember, remoteList);
|
||||||
}
|
}
|
||||||
for (LocalMember i : gossipManager.getDeadMembers()) {
|
for (LocalMember i : gossipManager.getDeadMembers()) {
|
||||||
@ -228,26 +233,28 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
log.debug(gossipManager.getMyself() + " contacted by dead member " + senderMember.getUri());
|
log.debug(gossipManager.getMyself() + " contacted by dead member " + senderMember.getUri());
|
||||||
i.recordHeartbeat(senderMember.getHeartbeat());
|
i.recordHeartbeat(senderMember.getHeartbeat());
|
||||||
i.setHeartbeat(senderMember.getHeartbeat());
|
i.setHeartbeat(senderMember.getHeartbeat());
|
||||||
//TODO consider forcing an UP here
|
// TODO consider forcing an UP here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Member remoteMember : remoteList) {
|
for (Member remoteMember : remoteList) {
|
||||||
if (remoteMember.getId().equals(gossipManager.getMyself().getId())) {
|
if (remoteMember.getId().equals(gossipManager.getMyself().getId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LocalMember aNewMember = new LocalMember(remoteMember.getClusterName(),
|
LocalMember aNewMember =
|
||||||
remoteMember.getUri(),
|
new LocalMember(
|
||||||
remoteMember.getId(),
|
remoteMember.getClusterName(),
|
||||||
remoteMember.getHeartbeat(),
|
remoteMember.getUri(),
|
||||||
remoteMember.getProperties(),
|
remoteMember.getId(),
|
||||||
gossipManager.getSettings().getWindowSize(),
|
remoteMember.getHeartbeat(),
|
||||||
gossipManager.getSettings().getMinimumSamples(),
|
remoteMember.getProperties(),
|
||||||
gossipManager.getSettings().getDistribution());
|
gossipManager.getSettings().getWindowSize(),
|
||||||
|
gossipManager.getSettings().getMinimumSamples(),
|
||||||
|
gossipManager.getSettings().getDistribution());
|
||||||
aNewMember.recordHeartbeat(remoteMember.getHeartbeat());
|
aNewMember.recordHeartbeat(remoteMember.getHeartbeat());
|
||||||
Object result = gossipManager.getMembers().putIfAbsent(aNewMember, GossipState.UP);
|
Object result = gossipManager.getMembers().putIfAbsent(aNewMember, GossipState.UP);
|
||||||
if (result != null){
|
if (result != null) {
|
||||||
for (Entry<LocalMember, GossipState> localMember : gossipManager.getMembers().entrySet()){
|
for (Entry<LocalMember, GossipState> localMember : gossipManager.getMembers().entrySet()) {
|
||||||
if (localMember.getKey().getId().equals(remoteMember.getId())){
|
if (localMember.getKey().getId().equals(remoteMember.getId())) {
|
||||||
localMember.getKey().recordHeartbeat(remoteMember.getHeartbeat());
|
localMember.getKey().recordHeartbeat(remoteMember.getHeartbeat());
|
||||||
localMember.getKey().setHeartbeat(remoteMember.getHeartbeat());
|
localMember.getKey().setHeartbeat(remoteMember.getHeartbeat());
|
||||||
localMember.getKey().setProperties(remoteMember.getProperties());
|
localMember.getKey().setProperties(remoteMember.getProperties());
|
||||||
@ -255,28 +262,37 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()){
|
if (log.isDebugEnabled()) {
|
||||||
debugState(senderMember, remoteList);
|
debugState(senderMember, remoteList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void debugState(RemoteMember senderMember,
|
private void debugState(RemoteMember senderMember, List<Member> remoteList) {
|
||||||
List<Member> remoteList){
|
|
||||||
log.warn(
|
log.warn(
|
||||||
"-----------------------\n" +
|
"-----------------------\n"
|
||||||
"Me " + gossipManager.getMyself() + "\n" +
|
+ "Me "
|
||||||
"Sender " + senderMember + "\n" +
|
+ gossipManager.getMyself()
|
||||||
"RemoteList " + remoteList + "\n" +
|
+ "\n"
|
||||||
"Live " + gossipManager.getLiveMembers()+ "\n" +
|
+ "Sender "
|
||||||
"Dead " + gossipManager.getDeadMembers()+ "\n" +
|
+ senderMember
|
||||||
"=======================");
|
+ "\n"
|
||||||
|
+ "RemoteList "
|
||||||
|
+ remoteList
|
||||||
|
+ "\n"
|
||||||
|
+ "Live "
|
||||||
|
+ gossipManager.getLiveMembers()
|
||||||
|
+ "\n"
|
||||||
|
+ "Dead "
|
||||||
|
+ gossipManager.getDeadMembers()
|
||||||
|
+ "\n"
|
||||||
|
+ "=======================");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Crdt merge(SharedDataMessage message) {
|
public Crdt merge(SharedDataMessage message) {
|
||||||
for (;;){
|
for (; ; ) {
|
||||||
SharedDataMessage previous = sharedData.putIfAbsent(message.getKey(), message);
|
SharedDataMessage previous = sharedData.putIfAbsent(message.getKey(), message);
|
||||||
if (previous == null){
|
if (previous == null) {
|
||||||
return (Crdt) message.getPayload();
|
return (Crdt) message.getPayload();
|
||||||
}
|
}
|
||||||
SharedDataMessage copy = new SharedDataMessage();
|
SharedDataMessage copy = new SharedDataMessage();
|
||||||
@ -288,35 +304,34 @@ public class GossipCore implements GossipCoreConstants {
|
|||||||
Crdt merged = ((Crdt) previous.getPayload()).merge((Crdt) message.getPayload());
|
Crdt merged = ((Crdt) previous.getPayload()).merge((Crdt) message.getPayload());
|
||||||
copy.setPayload(merged);
|
copy.setPayload(merged);
|
||||||
boolean replaced = sharedData.replace(message.getKey(), previous, copy);
|
boolean replaced = sharedData.replace(message.getKey(), previous, copy);
|
||||||
if (replaced){
|
if (replaced) {
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerPerNodeDataSubscriber(UpdateNodeDataEventHandler handler){
|
void registerPerNodeDataSubscriber(UpdateNodeDataEventHandler handler) {
|
||||||
eventManager.registerPerNodeDataSubscriber(handler);
|
eventManager.registerPerNodeDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerSharedDataSubscriber(UpdateSharedDataEventHandler handler){
|
void registerSharedDataSubscriber(UpdateSharedDataEventHandler handler) {
|
||||||
eventManager.registerSharedDataSubscriber(handler);
|
eventManager.registerSharedDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterPerNodeDataSubscriber(UpdateNodeDataEventHandler handler){
|
void unregisterPerNodeDataSubscriber(UpdateNodeDataEventHandler handler) {
|
||||||
eventManager.unregisterPerNodeDataSubscriber(handler);
|
eventManager.unregisterPerNodeDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterSharedDataSubscriber(UpdateSharedDataEventHandler handler){
|
void unregisterSharedDataSubscriber(UpdateSharedDataEventHandler handler) {
|
||||||
eventManager.unregisterSharedDataSubscriber(handler);
|
eventManager.unregisterSharedDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LatchAndBase {
|
static class LatchAndBase {
|
||||||
private final CountDownLatch latch;
|
private final CountDownLatch latch;
|
||||||
private volatile Base base;
|
private volatile Base base;
|
||||||
|
|
||||||
LatchAndBase(){
|
LatchAndBase() {
|
||||||
latch = new CountDownLatch(1);
|
latch = new CountDownLatch(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,15 +56,16 @@ import org.apache.gossip.utils.ReflectionUtils;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class GossipManager {
|
public class GossipManager {
|
||||||
|
|
||||||
|
|
||||||
// this mapper is used for ring and user-data persistence only. NOT messages.
|
// this mapper is used for ring and user-data persistence only. NOT messages.
|
||||||
public static final ObjectMapper metdataObjectMapper = new ObjectMapper() {
|
public static final ObjectMapper metdataObjectMapper =
|
||||||
@Serial
|
new ObjectMapper() {
|
||||||
private static final long serialVersionUID = 1L;
|
@Serial private static final long serialVersionUID = 1L;
|
||||||
{
|
|
||||||
enableDefaultTyping();
|
{
|
||||||
configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, false);
|
enableDefaultTyping();
|
||||||
}};
|
configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final ConcurrentSkipListMap<LocalMember, GossipState> members;
|
private final ConcurrentSkipListMap<LocalMember, GossipState> members;
|
||||||
private final LocalMember me;
|
private final LocalMember me;
|
||||||
@ -83,27 +84,47 @@ public class GossipManager {
|
|||||||
private TransportManager transportManager;
|
private TransportManager transportManager;
|
||||||
private ProtocolManager protocolManager;
|
private ProtocolManager protocolManager;
|
||||||
|
|
||||||
public GossipManager(String cluster,
|
public GossipManager(
|
||||||
URI uri, String id, Map<String, String> properties, GossipSettings settings,
|
String cluster,
|
||||||
List<Member> gossipMembers, GossipListener listener, MetricRegistry registry,
|
URI uri,
|
||||||
MessageHandler messageHandler) {
|
String id,
|
||||||
|
Map<String, String> properties,
|
||||||
|
GossipSettings settings,
|
||||||
|
List<Member> gossipMembers,
|
||||||
|
GossipListener listener,
|
||||||
|
MetricRegistry registry,
|
||||||
|
MessageHandler messageHandler) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
|
|
||||||
clock = new SystemClock();
|
clock = new SystemClock();
|
||||||
me = new LocalMember(cluster, uri, id, clock.nanoTime(), properties,
|
me =
|
||||||
settings.getWindowSize(), settings.getMinimumSamples(), settings.getDistribution());
|
new LocalMember(
|
||||||
|
cluster,
|
||||||
|
uri,
|
||||||
|
id,
|
||||||
|
clock.nanoTime(),
|
||||||
|
properties,
|
||||||
|
settings.getWindowSize(),
|
||||||
|
settings.getMinimumSamples(),
|
||||||
|
settings.getDistribution());
|
||||||
gossipCore = new GossipCore(this, registry);
|
gossipCore = new GossipCore(this, registry);
|
||||||
this.lockManager = new LockManager(this, settings.getLockManagerSettings(), registry);
|
this.lockManager = new LockManager(this, settings.getLockManagerSettings(), registry);
|
||||||
dataReaper = new DataReaper(gossipCore, clock);
|
dataReaper = new DataReaper(gossipCore, clock);
|
||||||
members = new ConcurrentSkipListMap<>();
|
members = new ConcurrentSkipListMap<>();
|
||||||
for (Member startupMember : gossipMembers) {
|
for (Member startupMember : gossipMembers) {
|
||||||
if (!startupMember.equals(me)) {
|
if (!startupMember.equals(me)) {
|
||||||
LocalMember member = new LocalMember(startupMember.getClusterName(),
|
LocalMember member =
|
||||||
startupMember.getUri(), startupMember.getId(),
|
new LocalMember(
|
||||||
clock.nanoTime(), startupMember.getProperties(), settings.getWindowSize(),
|
startupMember.getClusterName(),
|
||||||
settings.getMinimumSamples(), settings.getDistribution());
|
startupMember.getUri(),
|
||||||
//TODO should members start in down state?
|
startupMember.getId(),
|
||||||
|
clock.nanoTime(),
|
||||||
|
startupMember.getProperties(),
|
||||||
|
settings.getWindowSize(),
|
||||||
|
settings.getMinimumSamples(),
|
||||||
|
settings.getDistribution());
|
||||||
|
// TODO should members start in down state?
|
||||||
members.put(member, GossipState.DOWN);
|
members.put(member, GossipState.DOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,28 +132,45 @@ public class GossipManager {
|
|||||||
this.scheduledServiced = Executors.newScheduledThreadPool(1);
|
this.scheduledServiced = Executors.newScheduledThreadPool(1);
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
this.ringState = new RingStatePersister(GossipManager.buildRingStatePath(this), this);
|
this.ringState = new RingStatePersister(GossipManager.buildRingStatePath(this), this);
|
||||||
this.userDataState = new UserDataPersister(
|
this.userDataState =
|
||||||
gossipCore,
|
new UserDataPersister(
|
||||||
GossipManager.buildPerNodeDataPath(this),
|
gossipCore,
|
||||||
GossipManager.buildSharedDataPath(this));
|
GossipManager.buildPerNodeDataPath(this),
|
||||||
this.memberStateRefresher = new GossipMemberStateRefresher(members, settings, listener, this::findPerNodeGossipData);
|
GossipManager.buildSharedDataPath(this));
|
||||||
|
this.memberStateRefresher =
|
||||||
|
new GossipMemberStateRefresher(members, settings, listener, this::findPerNodeGossipData);
|
||||||
readSavedRingState();
|
readSavedRingState();
|
||||||
readSavedDataState();
|
readSavedDataState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File buildRingStatePath(GossipManager manager) {
|
public static File buildRingStatePath(GossipManager manager) {
|
||||||
return new File(manager.getSettings().getPathToRingState(), "ringstate." + manager.getMyself().getClusterName() + "."
|
return new File(
|
||||||
+ manager.getMyself().getId() + ".json");
|
manager.getSettings().getPathToRingState(),
|
||||||
|
"ringstate."
|
||||||
|
+ manager.getMyself().getClusterName()
|
||||||
|
+ "."
|
||||||
|
+ manager.getMyself().getId()
|
||||||
|
+ ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File buildSharedDataPath(GossipManager manager){
|
public static File buildSharedDataPath(GossipManager manager) {
|
||||||
return new File(manager.getSettings().getPathToDataState(), "shareddata."
|
return new File(
|
||||||
+ manager.getMyself().getClusterName() + "." + manager.getMyself().getId() + ".json");
|
manager.getSettings().getPathToDataState(),
|
||||||
|
"shareddata."
|
||||||
|
+ manager.getMyself().getClusterName()
|
||||||
|
+ "."
|
||||||
|
+ manager.getMyself().getId()
|
||||||
|
+ ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File buildPerNodeDataPath(GossipManager manager) {
|
public static File buildPerNodeDataPath(GossipManager manager) {
|
||||||
return new File(manager.getSettings().getPathToDataState(), "pernodedata."
|
return new File(
|
||||||
+ manager.getMyself().getClusterName() + "." + manager.getMyself().getId() + ".json");
|
manager.getSettings().getPathToDataState(),
|
||||||
|
"pernodedata."
|
||||||
|
+ manager.getMyself().getClusterName()
|
||||||
|
+ "."
|
||||||
|
+ manager.getMyself().getId()
|
||||||
|
+ ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageHandler getMessageHandler() {
|
public MessageHandler getMessageHandler() {
|
||||||
@ -152,22 +190,21 @@ public class GossipManager {
|
|||||||
*/
|
*/
|
||||||
public List<LocalMember> getDeadMembers() {
|
public List<LocalMember> getDeadMembers() {
|
||||||
return Collections.unmodifiableList(
|
return Collections.unmodifiableList(
|
||||||
members.entrySet()
|
members.entrySet().stream()
|
||||||
.stream()
|
.filter(entry -> GossipState.DOWN.equals(entry.getValue()))
|
||||||
.filter(entry -> GossipState.DOWN.equals(entry.getValue()))
|
.map(Entry::getKey)
|
||||||
.map(Entry::getKey).collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return a read only list of members found in the UP state
|
* @return a read only list of members found in the UP state
|
||||||
*/
|
*/
|
||||||
public List<LocalMember> getLiveMembers() {
|
public List<LocalMember> getLiveMembers() {
|
||||||
return Collections.unmodifiableList(
|
return Collections.unmodifiableList(
|
||||||
members.entrySet()
|
members.entrySet().stream()
|
||||||
.stream()
|
.filter(entry -> GossipState.UP.equals(entry.getValue()))
|
||||||
.filter(entry -> GossipState.UP.equals(entry.getValue()))
|
.map(Entry::getKey)
|
||||||
.map(Entry::getKey).collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMember getMyself() {
|
public LocalMember getMyself() {
|
||||||
@ -183,17 +220,17 @@ public class GossipManager {
|
|||||||
// protocol manager and transport managers are specified in settings.
|
// protocol manager and transport managers are specified in settings.
|
||||||
// construct them here via reflection.
|
// construct them here via reflection.
|
||||||
|
|
||||||
protocolManager = ReflectionUtils.constructWithReflection(
|
protocolManager =
|
||||||
settings.getProtocolManagerClass(),
|
ReflectionUtils.constructWithReflection(
|
||||||
new Class<?>[] { GossipSettings.class, String.class, MetricRegistry.class },
|
settings.getProtocolManagerClass(),
|
||||||
new Object[] { settings, me.getId(), this.getRegistry() }
|
new Class<?>[] {GossipSettings.class, String.class, MetricRegistry.class},
|
||||||
);
|
new Object[] {settings, me.getId(), this.getRegistry()});
|
||||||
|
|
||||||
transportManager = ReflectionUtils.constructWithReflection(
|
transportManager =
|
||||||
settings.getTransportManagerClass(),
|
ReflectionUtils.constructWithReflection(
|
||||||
new Class<?>[] { GossipManager.class, GossipCore.class},
|
settings.getTransportManagerClass(),
|
||||||
new Object[] { this, gossipCore }
|
new Class<?>[] {GossipManager.class, GossipCore.class},
|
||||||
);
|
new Object[] {this, gossipCore});
|
||||||
|
|
||||||
// start processing gossip messages.
|
// start processing gossip messages.
|
||||||
transportManager.startEndpoint();
|
transportManager.startEndpoint();
|
||||||
@ -213,10 +250,16 @@ public class GossipManager {
|
|||||||
private void readSavedRingState() {
|
private void readSavedRingState() {
|
||||||
if (settings.isPersistRingState()) {
|
if (settings.isPersistRingState()) {
|
||||||
for (LocalMember l : ringState.readFromDisk()) {
|
for (LocalMember l : ringState.readFromDisk()) {
|
||||||
LocalMember member = new LocalMember(l.getClusterName(),
|
LocalMember member =
|
||||||
l.getUri(), l.getId(),
|
new LocalMember(
|
||||||
clock.nanoTime(), l.getProperties(), settings.getWindowSize(),
|
l.getClusterName(),
|
||||||
settings.getMinimumSamples(), settings.getDistribution());
|
l.getUri(),
|
||||||
|
l.getId(),
|
||||||
|
clock.nanoTime(),
|
||||||
|
l.getProperties(),
|
||||||
|
settings.getWindowSize(),
|
||||||
|
settings.getMinimumSamples(),
|
||||||
|
settings.getDistribution());
|
||||||
members.putIfAbsent(member, GossipState.DOWN);
|
members.putIfAbsent(member, GossipState.DOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,7 +267,8 @@ public class GossipManager {
|
|||||||
|
|
||||||
private void readSavedDataState() {
|
private void readSavedDataState() {
|
||||||
if (settings.isPersistDataState()) {
|
if (settings.isPersistDataState()) {
|
||||||
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> l : userDataState.readPerNodeFromDisk().entrySet()) {
|
for (Entry<String, ConcurrentHashMap<String, PerNodeDataMessage>> l :
|
||||||
|
userDataState.readPerNodeFromDisk().entrySet()) {
|
||||||
for (Entry<String, PerNodeDataMessage> j : l.getValue().entrySet()) {
|
for (Entry<String, PerNodeDataMessage> j : l.getValue().entrySet()) {
|
||||||
gossipCore.addPerNodeData(j.getValue());
|
gossipCore.addPerNodeData(j.getValue());
|
||||||
}
|
}
|
||||||
@ -237,9 +281,7 @@ public class GossipManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Shutdown the gossip service. */
|
||||||
* Shutdown the gossip service.
|
|
||||||
*/
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
gossipServiceRunning.set(false);
|
gossipServiceRunning.set(false);
|
||||||
lockManager.shutdown();
|
lockManager.shutdown();
|
||||||
@ -256,7 +298,7 @@ public class GossipManager {
|
|||||||
scheduledServiced.shutdownNow();
|
scheduledServiced.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void gossipPerNodeData(PerNodeDataMessage message){
|
public void gossipPerNodeData(PerNodeDataMessage message) {
|
||||||
Objects.nonNull(message.getKey());
|
Objects.nonNull(message.getKey());
|
||||||
Objects.nonNull(message.getTimestamp());
|
Objects.nonNull(message.getTimestamp());
|
||||||
Objects.nonNull(message.getPayload());
|
Objects.nonNull(message.getPayload());
|
||||||
@ -264,7 +306,7 @@ public class GossipManager {
|
|||||||
gossipCore.addPerNodeData(message);
|
gossipCore.addPerNodeData(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void gossipSharedData(SharedDataMessage message){
|
public void gossipSharedData(SharedDataMessage message) {
|
||||||
Objects.nonNull(message.getKey());
|
Objects.nonNull(message.getKey());
|
||||||
Objects.nonNull(message.getTimestamp());
|
Objects.nonNull(message.getTimestamp());
|
||||||
Objects.nonNull(message.getPayload());
|
Objects.nonNull(message.getPayload());
|
||||||
@ -273,12 +315,12 @@ public class GossipManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Crdt findCrdt(String key){
|
public Crdt findCrdt(String key) {
|
||||||
SharedDataMessage l = gossipCore.getSharedData().get(key);
|
SharedDataMessage l = gossipCore.getSharedData().get(key);
|
||||||
if (l == null){
|
if (l == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (l.getExpireAt() < clock.currentTimeMillis()){
|
if (l.getExpireAt() < clock.currentTimeMillis()) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return (Crdt) l.getPayload();
|
return (Crdt) l.getPayload();
|
||||||
@ -286,24 +328,24 @@ public class GossipManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Crdt merge(SharedDataMessage message){
|
public Crdt merge(SharedDataMessage message) {
|
||||||
Objects.nonNull(message.getKey());
|
Objects.nonNull(message.getKey());
|
||||||
Objects.nonNull(message.getTimestamp());
|
Objects.nonNull(message.getTimestamp());
|
||||||
Objects.nonNull(message.getPayload());
|
Objects.nonNull(message.getPayload());
|
||||||
message.setNodeId(me.getId());
|
message.setNodeId(me.getId());
|
||||||
if (! (message.getPayload() instanceof Crdt)){
|
if (!(message.getPayload() instanceof Crdt)) {
|
||||||
throw new IllegalArgumentException("Not a subclass of CRDT " + message.getPayload());
|
throw new IllegalArgumentException("Not a subclass of CRDT " + message.getPayload());
|
||||||
}
|
}
|
||||||
return gossipCore.merge(message);
|
return gossipCore.merge(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PerNodeDataMessage findPerNodeGossipData(String nodeId, String key){
|
public PerNodeDataMessage findPerNodeGossipData(String nodeId, String key) {
|
||||||
ConcurrentHashMap<String, PerNodeDataMessage> j = gossipCore.getPerNodeData().get(nodeId);
|
ConcurrentHashMap<String, PerNodeDataMessage> j = gossipCore.getPerNodeData().get(nodeId);
|
||||||
if (j == null){
|
if (j == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
PerNodeDataMessage l = j.get(key);
|
PerNodeDataMessage l = j.get(key);
|
||||||
if (l == null){
|
if (l == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (l.getExpireAt() != null && l.getExpireAt() < clock.currentTimeMillis()) {
|
if (l.getExpireAt() != null && l.getExpireAt() < clock.currentTimeMillis()) {
|
||||||
@ -313,12 +355,12 @@ public class GossipManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedDataMessage findSharedGossipData(String key){
|
public SharedDataMessage findSharedGossipData(String key) {
|
||||||
SharedDataMessage l = gossipCore.getSharedData().get(key);
|
SharedDataMessage l = gossipCore.getSharedData().get(key);
|
||||||
if (l == null){
|
if (l == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (l.getExpireAt() < clock.currentTimeMillis()){
|
if (l.getExpireAt() < clock.currentTimeMillis()) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return l;
|
return l;
|
||||||
@ -344,34 +386,34 @@ public class GossipManager {
|
|||||||
public Clock getClock() {
|
public Clock getClock() {
|
||||||
return clock;
|
return clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: consider making these path methods part of GossipSettings
|
// todo: consider making these path methods part of GossipSettings
|
||||||
|
|
||||||
public MetricRegistry getRegistry() {
|
public MetricRegistry getRegistry() {
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProtocolManager getProtocolManager() {
|
public ProtocolManager getProtocolManager() {
|
||||||
return protocolManager;
|
return protocolManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportManager getTransportManager() {
|
public TransportManager getTransportManager() {
|
||||||
return transportManager;
|
return transportManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerPerNodeDataSubscriber(UpdateNodeDataEventHandler handler){
|
public void registerPerNodeDataSubscriber(UpdateNodeDataEventHandler handler) {
|
||||||
gossipCore.registerPerNodeDataSubscriber(handler);
|
gossipCore.registerPerNodeDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerSharedDataSubscriber(UpdateSharedDataEventHandler handler){
|
public void registerSharedDataSubscriber(UpdateSharedDataEventHandler handler) {
|
||||||
gossipCore.registerSharedDataSubscriber(handler);
|
gossipCore.registerSharedDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterPerNodeDataSubscriber(UpdateNodeDataEventHandler handler){
|
public void unregisterPerNodeDataSubscriber(UpdateNodeDataEventHandler handler) {
|
||||||
gossipCore.unregisterPerNodeDataSubscriber(handler);
|
gossipCore.unregisterPerNodeDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterSharedDataSubscriber(UpdateSharedDataEventHandler handler){
|
public void unregisterSharedDataSubscriber(UpdateSharedDataEventHandler handler) {
|
||||||
gossipCore.unregisterSharedDataSubscriber(handler);
|
gossipCore.unregisterSharedDataSubscriber(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +423,7 @@ public class GossipManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the lock manager specified with this GossipManager.
|
* Get the lock manager specified with this GossipManager.
|
||||||
|
*
|
||||||
* @return lock manager object.
|
* @return lock manager object.
|
||||||
*/
|
*/
|
||||||
public LockManager getLockManager() {
|
public LockManager getLockManager() {
|
||||||
@ -389,10 +432,11 @@ public class GossipManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to acquire a lock on given shared data key.
|
* Try to acquire a lock on given shared data key.
|
||||||
|
*
|
||||||
* @param key key of tha share data object.
|
* @param key key of tha share data object.
|
||||||
* @throws VoteFailedException if the locking is failed.
|
* @throws VoteFailedException if the locking is failed.
|
||||||
*/
|
*/
|
||||||
public void acquireSharedDataLock(String key) throws VoteFailedException{
|
public void acquireSharedDataLock(String key) throws VoteFailedException {
|
||||||
lockManager.acquireSharedDataLock(key);
|
lockManager.acquireSharedDataLock(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,17 @@
|
|||||||
package org.apache.gossip.manager;
|
package org.apache.gossip.manager;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import org.apache.gossip.GossipSettings;
|
|
||||||
import org.apache.gossip.Member;
|
|
||||||
import org.apache.gossip.StartupSettings;
|
|
||||||
import org.apache.gossip.event.GossipListener;
|
|
||||||
import org.apache.gossip.event.GossipState;
|
|
||||||
import org.apache.gossip.manager.handlers.MessageHandler;
|
|
||||||
import org.apache.gossip.manager.handlers.MessageHandlerFactory;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.apache.gossip.GossipSettings;
|
||||||
|
import org.apache.gossip.Member;
|
||||||
|
import org.apache.gossip.StartupSettings;
|
||||||
|
import org.apache.gossip.event.GossipListener;
|
||||||
|
import org.apache.gossip.manager.handlers.MessageHandler;
|
||||||
|
import org.apache.gossip.manager.handlers.MessageHandlerFactory;
|
||||||
|
|
||||||
public class GossipManagerBuilder {
|
public class GossipManagerBuilder {
|
||||||
|
|
||||||
|
@ -43,17 +43,20 @@ public class GossipMemberStateRefresher {
|
|||||||
private final ScheduledExecutorService scheduledExecutor;
|
private final ScheduledExecutorService scheduledExecutor;
|
||||||
private final BlockingQueue<Runnable> workQueue;
|
private final BlockingQueue<Runnable> workQueue;
|
||||||
|
|
||||||
public GossipMemberStateRefresher(Map<LocalMember, GossipState> members, GossipSettings settings,
|
public GossipMemberStateRefresher(
|
||||||
GossipListener listener,
|
Map<LocalMember, GossipState> members,
|
||||||
BiFunction<String, String, PerNodeDataMessage> findPerNodeGossipData) {
|
GossipSettings settings,
|
||||||
|
GossipListener listener,
|
||||||
|
BiFunction<String, String, PerNodeDataMessage> findPerNodeGossipData) {
|
||||||
this.members = members;
|
this.members = members;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
this.findPerNodeGossipData = findPerNodeGossipData;
|
this.findPerNodeGossipData = findPerNodeGossipData;
|
||||||
clock = new SystemClock();
|
clock = new SystemClock();
|
||||||
workQueue = new ArrayBlockingQueue<>(1024);
|
workQueue = new ArrayBlockingQueue<>(1024);
|
||||||
listenerExecutor = new ThreadPoolExecutor(1, 20, 1, TimeUnit.SECONDS, workQueue,
|
listenerExecutor =
|
||||||
new ThreadPoolExecutor.DiscardOldestPolicy());
|
new ThreadPoolExecutor(
|
||||||
|
1, 20, 1, TimeUnit.SECONDS, workQueue, new ThreadPoolExecutor.DiscardOldestPolicy());
|
||||||
scheduledExecutor = Executors.newScheduledThreadPool(1);
|
scheduledExecutor = Executors.newScheduledThreadPool(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +75,7 @@ public class GossipMemberStateRefresher {
|
|||||||
public void runOnce() {
|
public void runOnce() {
|
||||||
for (Entry<LocalMember, GossipState> entry : members.entrySet()) {
|
for (Entry<LocalMember, GossipState> entry : members.entrySet()) {
|
||||||
boolean userDown = processOptimisticShutdown(entry);
|
boolean userDown = processOptimisticShutdown(entry);
|
||||||
if (userDown)
|
if (userDown) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
Double phiMeasure = entry.getKey().detect(clock.nanoTime());
|
Double phiMeasure = entry.getKey().detect(clock.nanoTime());
|
||||||
GossipState requiredState;
|
GossipState requiredState;
|
||||||
@ -87,17 +89,15 @@ public class GossipMemberStateRefresher {
|
|||||||
if (entry.getValue() != requiredState) {
|
if (entry.getValue() != requiredState) {
|
||||||
members.put(entry.getKey(), requiredState);
|
members.put(entry.getKey(), requiredState);
|
||||||
/* Call listeners asynchronously */
|
/* Call listeners asynchronously */
|
||||||
for (GossipListener listener: listeners)
|
for (GossipListener listener : listeners)
|
||||||
listenerExecutor.execute(() -> listener.gossipEvent(entry.getKey(), requiredState));
|
listenerExecutor.execute(() -> listener.gossipEvent(entry.getKey(), requiredState));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GossipState calcRequiredState(Double phiMeasure) {
|
public GossipState calcRequiredState(Double phiMeasure) {
|
||||||
if (phiMeasure > settings.getConvictThreshold())
|
if (phiMeasure > settings.getConvictThreshold()) return GossipState.DOWN;
|
||||||
return GossipState.DOWN;
|
else return GossipState.UP;
|
||||||
else
|
|
||||||
return GossipState.UP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GossipState calcRequiredStateCleanupInterval(LocalMember member, GossipState state) {
|
public GossipState calcRequiredStateCleanupInterval(LocalMember member, GossipState state) {
|
||||||
@ -111,14 +111,15 @@ public class GossipMemberStateRefresher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If we have a special key the per-node data that means that the node has sent us
|
* If we have a special key the per-node data that means that the node has sent us a pre-emptive
|
||||||
* a pre-emptive shutdown message. We process this so node is seen down sooner
|
* shutdown message. We process this so node is seen down sooner
|
||||||
*
|
*
|
||||||
* @param l member to consider
|
* @param l member to consider
|
||||||
* @return true if node forced down
|
* @return true if node forced down
|
||||||
*/
|
*/
|
||||||
public boolean processOptimisticShutdown(Entry<LocalMember, GossipState> l) {
|
public boolean processOptimisticShutdown(Entry<LocalMember, GossipState> l) {
|
||||||
PerNodeDataMessage m = findPerNodeGossipData.apply(l.getKey().getId(), ShutdownMessage.PER_NODE_KEY);
|
PerNodeDataMessage m =
|
||||||
|
findPerNodeGossipData.apply(l.getKey().getId(), ShutdownMessage.PER_NODE_KEY);
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -126,7 +127,7 @@ public class GossipMemberStateRefresher {
|
|||||||
if (s.getShutdownAtNanos() > l.getKey().getHeartbeat()) {
|
if (s.getShutdownAtNanos() > l.getKey().getHeartbeat()) {
|
||||||
members.put(l.getKey(), GossipState.DOWN);
|
members.put(l.getKey(), GossipState.DOWN);
|
||||||
if (l.getValue() == GossipState.UP) {
|
if (l.getValue() == GossipState.UP) {
|
||||||
for (GossipListener listener: listeners)
|
for (GossipListener listener : listeners)
|
||||||
listenerExecutor.execute(() -> listener.gossipEvent(l.getKey(), GossipState.DOWN));
|
listenerExecutor.execute(() -> listener.gossipEvent(l.getKey(), GossipState.DOWN));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -153,4 +154,4 @@ public class GossipMemberStateRefresher {
|
|||||||
}
|
}
|
||||||
listenerExecutor.shutdownNow();
|
listenerExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.gossip.manager;
|
package org.apache.gossip.manager;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@ -35,21 +35,21 @@ public class RingStatePersister implements Runnable {
|
|||||||
// NOTE: this is a different instance than what gets used for message marshalling.
|
// NOTE: this is a different instance than what gets used for message marshalling.
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final GossipManager manager;
|
private final GossipManager manager;
|
||||||
|
|
||||||
public RingStatePersister(File path, GossipManager manager){
|
public RingStatePersister(File path, GossipManager manager) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.objectMapper = GossipManager.metdataObjectMapper;
|
this.objectMapper = GossipManager.metdataObjectMapper;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
writeToDisk();
|
writeToDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToDisk() {
|
void writeToDisk() {
|
||||||
NavigableSet<LocalMember> i = manager.getMembers().keySet();
|
NavigableSet<LocalMember> i = manager.getMembers().keySet();
|
||||||
try (FileOutputStream fos = new FileOutputStream(path)){
|
try (FileOutputStream fos = new FileOutputStream(path)) {
|
||||||
objectMapper.writeValue(fos, i);
|
objectMapper.writeValue(fos, i);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error!", e);
|
log.error("Error!", e);
|
||||||
@ -61,7 +61,7 @@ public class RingStatePersister implements Runnable {
|
|||||||
if (!path.exists()) {
|
if (!path.exists()) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
try (FileInputStream fos = new FileInputStream(path)){
|
try (FileInputStream fos = new FileInputStream(path)) {
|
||||||
return objectMapper.readValue(fos, ArrayList.class);
|
return objectMapper.readValue(fos, ArrayList.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error", e);
|
log.error("Error", e);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.manager;
|
package org.apache.gossip.manager;
|
||||||
|
|
||||||
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@ -24,12 +25,9 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.gossip.LocalMember;
|
import org.apache.gossip.LocalMember;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
|
||||||
|
|
||||||
/** Base implementation gossips randomly to live nodes periodically gossips to dead ones */
|
/** Base implementation gossips randomly to live nodes periodically gossips to dead ones */
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SimpleActiveGossiper extends AbstractActiveGossiper {
|
public class SimpleActiveGossiper extends AbstractActiveGossiper {
|
||||||
|
@ -17,14 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.manager;
|
package org.apache.gossip.manager;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.extern.java.Log;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.gossip.model.PerNodeDataMessage;
|
import org.apache.gossip.model.PerNodeDataMessage;
|
||||||
import org.apache.gossip.model.SharedDataMessage;
|
import org.apache.gossip.model.SharedDataMessage;
|
||||||
@ -48,14 +46,14 @@ public class UserDataPersister implements Runnable {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>> readPerNodeFromDisk() {
|
ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>> readPerNodeFromDisk() {
|
||||||
if (!perNodePath.exists()) {
|
if (!perNodePath.exists()) {
|
||||||
return new ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>>();
|
return new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
try (FileInputStream fos = new FileInputStream(perNodePath)) {
|
try (FileInputStream fos = new FileInputStream(perNodePath)) {
|
||||||
return objectMapper.readValue(fos, ConcurrentHashMap.class);
|
return objectMapper.readValue(fos, ConcurrentHashMap.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error!", e);
|
log.error("Error!", e);
|
||||||
}
|
}
|
||||||
return new ConcurrentHashMap<String, ConcurrentHashMap<String, PerNodeDataMessage>>();
|
return new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writePerNodeToDisk() {
|
void writePerNodeToDisk() {
|
||||||
|
@ -33,7 +33,7 @@ import org.apache.gossip.udp.UdpNotAMemberFault;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ActiveGossipMessageHandler implements MessageHandler {
|
public class ActiveGossipMessageHandler implements MessageHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param gossipCore context.
|
* @param gossipCore context.
|
||||||
* @param gossipManager context.
|
* @param gossipManager context.
|
||||||
@ -53,7 +53,8 @@ public class ActiveGossipMessageHandler implements MessageHandler {
|
|||||||
log.debug("Gossip message with faulty URI", e);
|
log.debug("Gossip message with faulty URI", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
RemoteMember member = new RemoteMember(
|
RemoteMember member =
|
||||||
|
new RemoteMember(
|
||||||
activeGossipMessage.getMembers().get(i).getCluster(),
|
activeGossipMessage.getMembers().get(i).getCluster(),
|
||||||
u,
|
u,
|
||||||
activeGossipMessage.getMembers().get(i).getId(),
|
activeGossipMessage.getMembers().get(i).getId(),
|
||||||
|
@ -26,22 +26,17 @@ import org.apache.gossip.udp.UdpNotAMemberFault;
|
|||||||
import org.apache.gossip.udp.UdpPerNodeDataBulkMessage;
|
import org.apache.gossip.udp.UdpPerNodeDataBulkMessage;
|
||||||
import org.apache.gossip.udp.UdpSharedDataBulkMessage;
|
import org.apache.gossip.udp.UdpSharedDataBulkMessage;
|
||||||
|
|
||||||
@JsonTypeInfo(
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
use = JsonTypeInfo.Id.CLASS,
|
|
||||||
include = JsonTypeInfo.As.PROPERTY,
|
|
||||||
property = "type")
|
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@Type(value = ActiveGossipMessage.class, name = "ActiveGossipMessage"),
|
@Type(value = ActiveGossipMessage.class, name = "ActiveGossipMessage"),
|
||||||
@Type(value = Fault.class, name = "Fault"),
|
@Type(value = Fault.class, name = "Fault"),
|
||||||
@Type(value = ActiveGossipOk.class, name = "ActiveGossipOk"),
|
@Type(value = ActiveGossipOk.class, name = "ActiveGossipOk"),
|
||||||
@Type(value = UdpActiveGossipOk.class, name = "UdpActiveGossipOk"),
|
@Type(value = UdpActiveGossipOk.class, name = "UdpActiveGossipOk"),
|
||||||
@Type(value = UdpActiveGossipMessage.class, name = "UdpActiveGossipMessage"),
|
@Type(value = UdpActiveGossipMessage.class, name = "UdpActiveGossipMessage"),
|
||||||
@Type(value = UdpNotAMemberFault.class, name = "UdpNotAMemberFault"),
|
@Type(value = UdpNotAMemberFault.class, name = "UdpNotAMemberFault"),
|
||||||
@Type(value = PerNodeDataMessage.class, name = "PerNodeDataMessage"),
|
@Type(value = PerNodeDataMessage.class, name = "PerNodeDataMessage"),
|
||||||
@Type(value = UdpPerNodeDataBulkMessage.class, name = "UdpPerNodeDataMessage"),
|
@Type(value = UdpPerNodeDataBulkMessage.class, name = "UdpPerNodeDataMessage"),
|
||||||
@Type(value = SharedDataMessage.class, name = "SharedDataMessage"),
|
@Type(value = SharedDataMessage.class, name = "SharedDataMessage"),
|
||||||
@Type(value = UdpSharedDataBulkMessage.class, name = "UdpSharedDataMessage")
|
@Type(value = UdpSharedDataBulkMessage.class, name = "UdpSharedDataMessage")
|
||||||
})
|
})
|
||||||
public class Base {
|
public class Base {}
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -31,47 +31,65 @@ public class SharedDataMessage extends Base {
|
|||||||
public String getNodeId() {
|
public String getNodeId() {
|
||||||
return nodeId;
|
return nodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNodeId(String nodeId) {
|
public void setNodeId(String nodeId) {
|
||||||
this.nodeId = nodeId;
|
this.nodeId = nodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKey(String key) {
|
public void setKey(String key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getPayload() {
|
public Object getPayload() {
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPayload(Object payload) {
|
public void setPayload(Object payload) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getTimestamp() {
|
public Long getTimestamp() {
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimestamp(Long timestamp) {
|
public void setTimestamp(Long timestamp) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getExpireAt() {
|
public Long getExpireAt() {
|
||||||
return expireAt;
|
return expireAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpireAt(Long expireAt) {
|
public void setExpireAt(Long expireAt) {
|
||||||
this.expireAt = expireAt;
|
this.expireAt = expireAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Replicable<SharedDataMessage> getReplicable() {
|
public Replicable<SharedDataMessage> getReplicable() {
|
||||||
return replicable;
|
return replicable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReplicable(Replicable<SharedDataMessage> replicable) {
|
public void setReplicable(Replicable<SharedDataMessage> replicable) {
|
||||||
this.replicable = replicable;
|
this.replicable = replicable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SharedGossipDataMessage [nodeId=" + nodeId + ", key=" + key + ", payload=" + payload
|
return "SharedGossipDataMessage [nodeId="
|
||||||
+ ", timestamp=" + timestamp + ", expireAt=" + expireAt
|
+ nodeId
|
||||||
+ ", replicable=" + replicable + "]";
|
+ ", key="
|
||||||
|
+ key
|
||||||
|
+ ", payload="
|
||||||
|
+ payload
|
||||||
|
+ ", timestamp="
|
||||||
|
+ timestamp
|
||||||
|
+ ", expireAt="
|
||||||
|
+ expireAt
|
||||||
|
+ ", replicable="
|
||||||
|
+ replicable
|
||||||
|
+ "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,9 @@ import org.apache.gossip.model.Base;
|
|||||||
/** interface for managing message marshaling. */
|
/** interface for managing message marshaling. */
|
||||||
public interface ProtocolManager {
|
public interface ProtocolManager {
|
||||||
|
|
||||||
/** serialize a message
|
/**
|
||||||
|
* serialize a message
|
||||||
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* @return serialized message.
|
* @return serialized message.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
@ -32,6 +34,7 @@ public interface ProtocolManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the next message from a byte source.
|
* Reads the next message from a byte source.
|
||||||
|
*
|
||||||
* @param buf
|
* @param buf
|
||||||
* @return a gossip message.
|
* @return a gossip message.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
@ -17,11 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.replication;
|
package org.apache.gossip.replication;
|
||||||
|
|
||||||
import org.apache.gossip.LocalMember;
|
|
||||||
import org.apache.gossip.model.Base;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.apache.gossip.LocalMember;
|
||||||
|
import org.apache.gossip.model.Base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replicable implementation which does not replicate data to given set of nodes.
|
* Replicable implementation which does not replicate data to given set of nodes.
|
||||||
|
@ -29,9 +29,9 @@ import org.apache.gossip.model.Base;
|
|||||||
* @see Replicable
|
* @see Replicable
|
||||||
*/
|
*/
|
||||||
public class WhiteListReplicable<T extends Base> implements Replicable<T> {
|
public class WhiteListReplicable<T extends Base> implements Replicable<T> {
|
||||||
|
|
||||||
private final List<LocalMember> whiteListMembers;
|
private final List<LocalMember> whiteListMembers;
|
||||||
|
|
||||||
public WhiteListReplicable(List<LocalMember> whiteListMembers) {
|
public WhiteListReplicable(List<LocalMember> whiteListMembers) {
|
||||||
if (whiteListMembers == null) {
|
if (whiteListMembers == null) {
|
||||||
this.whiteListMembers = new ArrayList<>();
|
this.whiteListMembers = new ArrayList<>();
|
||||||
|
@ -27,9 +27,7 @@ import org.apache.gossip.manager.GossipCore;
|
|||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.utils.ReflectionUtils;
|
import org.apache.gossip.utils.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/** Manage the protcol threads (active and passive gossipers). */
|
||||||
* Manage the protcol threads (active and passive gossipers).
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractTransportManager implements TransportManager {
|
public abstract class AbstractTransportManager implements TransportManager {
|
||||||
|
|
||||||
@ -37,19 +35,16 @@ public abstract class AbstractTransportManager implements TransportManager {
|
|||||||
protected final GossipCore gossipCore;
|
protected final GossipCore gossipCore;
|
||||||
private final ExecutorService gossipThreadExecutor;
|
private final ExecutorService gossipThreadExecutor;
|
||||||
private final AbstractActiveGossiper activeGossipThread;
|
private final AbstractActiveGossiper activeGossipThread;
|
||||||
|
|
||||||
public AbstractTransportManager(GossipManager gossipManager, GossipCore gossipCore) {
|
public AbstractTransportManager(GossipManager gossipManager, GossipCore gossipCore) {
|
||||||
this.gossipManager = gossipManager;
|
this.gossipManager = gossipManager;
|
||||||
this.gossipCore = gossipCore;
|
this.gossipCore = gossipCore;
|
||||||
gossipThreadExecutor = Executors.newCachedThreadPool();
|
gossipThreadExecutor = Executors.newCachedThreadPool();
|
||||||
activeGossipThread = ReflectionUtils.constructWithReflection(
|
activeGossipThread =
|
||||||
gossipManager.getSettings().getActiveGossipClass(),
|
ReflectionUtils.constructWithReflection(
|
||||||
new Class<?>[]{
|
gossipManager.getSettings().getActiveGossipClass(),
|
||||||
GossipManager.class, GossipCore.class, MetricRegistry.class
|
new Class<?>[] {GossipManager.class, GossipCore.class, MetricRegistry.class},
|
||||||
},
|
new Object[] {gossipManager, gossipCore, gossipManager.getRegistry()});
|
||||||
new Object[]{
|
|
||||||
gossipManager, gossipCore, gossipManager.getRegistry()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// shut down threads etc.
|
// shut down threads etc.
|
||||||
|
@ -23,6 +23,7 @@ public class UdpNotAMemberFault extends NotAMemberFault implements Trackable {
|
|||||||
|
|
||||||
private String uriFrom;
|
private String uriFrom;
|
||||||
private String uuid;
|
private String uuid;
|
||||||
|
|
||||||
public UdpNotAMemberFault() {}
|
public UdpNotAMemberFault() {}
|
||||||
|
|
||||||
public String getUriFrom() {
|
public String getUriFrom() {
|
||||||
|
@ -23,7 +23,6 @@ import java.net.URISyntaxException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -27,16 +27,18 @@ public class MemberTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHashCodeFromGossip40() throws URISyntaxException {
|
public void testHashCodeFromGossip40() throws URISyntaxException {
|
||||||
Assertions.assertNotEquals(new LocalMember(
|
Assertions.assertNotEquals(
|
||||||
"mycluster",
|
new LocalMember(
|
||||||
new URI("udp://4.4.4.4:1000"),
|
"mycluster",
|
||||||
"myid",
|
new URI("udp://4.4.4.4:1000"),
|
||||||
1,
|
"myid",
|
||||||
new HashMap<>(),
|
1,
|
||||||
10,
|
new HashMap<>(),
|
||||||
5,
|
10,
|
||||||
"exponential")
|
5,
|
||||||
.hashCode(), new LocalMember(
|
"exponential")
|
||||||
|
.hashCode(),
|
||||||
|
new LocalMember(
|
||||||
"mycluster",
|
"mycluster",
|
||||||
new URI("udp://4.4.4.5:1005"),
|
new URI("udp://4.4.4.5:1005"),
|
||||||
"yourid",
|
"yourid",
|
||||||
|
@ -54,20 +54,24 @@ public class FailureDetectorTest {
|
|||||||
}
|
}
|
||||||
Integer lastRecorded = values.get(values.size() - 2);
|
Integer lastRecorded = values.get(values.size() - 2);
|
||||||
|
|
||||||
//after "step" delay we need to be considered UP
|
// after "step" delay we need to be considered UP
|
||||||
Assertions.assertTrue(fd.computePhiMeasure(values.get(values.size() - 1)) < failureThreshold);
|
Assertions.assertTrue(fd.computePhiMeasure(values.get(values.size() - 1)) < failureThreshold);
|
||||||
|
|
||||||
//if we check phi-measure after mean delay we get value for 0.5 probability(normal distribution)
|
// if we check phi-measure after mean delay we get value for 0.5 probability(normal
|
||||||
Assertions.assertEquals(fd.computePhiMeasure(lastRecorded + Math.round(deltaSum / deltaCount)),
|
// distribution)
|
||||||
-Math.log10(0.5),
|
Assertions.assertEquals(
|
||||||
0.1);
|
fd.computePhiMeasure(lastRecorded + Math.round(deltaSum / deltaCount)),
|
||||||
|
-Math.log10(0.5),
|
||||||
|
0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkMinimumSamples() {
|
public void checkMinimumSamples() {
|
||||||
int minimumSamples = 5;
|
int minimumSamples = 5;
|
||||||
FailureDetector fd = new FailureDetector(minimumSamples, 1000, "normal");
|
FailureDetector fd = new FailureDetector(minimumSamples, 1000, "normal");
|
||||||
for (int i = 0; i < minimumSamples + 1; i++) { // +1 because we don't place first heartbeat into structure
|
for (int i = 0;
|
||||||
|
i < minimumSamples + 1;
|
||||||
|
i++) { // +1 because we don't place first heartbeat into structure
|
||||||
Assertions.assertNull(fd.computePhiMeasure(100));
|
Assertions.assertNull(fd.computePhiMeasure(100));
|
||||||
fd.recordHeartbeat(i);
|
fd.recordHeartbeat(i);
|
||||||
}
|
}
|
||||||
@ -77,28 +81,30 @@ public class FailureDetectorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void checkMonotonicDead() {
|
public void checkMonotonicDead() {
|
||||||
final FailureDetector fd = new FailureDetector(5, 1000, "normal");
|
final FailureDetector fd = new FailureDetector(5, 1000, "normal");
|
||||||
TriConsumer<Integer, Integer, Integer> checkAlive = (begin, end, step) -> {
|
TriConsumer<Integer, Integer, Integer> checkAlive =
|
||||||
List<Integer> times = generateTimeList(begin, end, step);
|
(begin, end, step) -> {
|
||||||
for (Integer time : times) {
|
List<Integer> times = generateTimeList(begin, end, step);
|
||||||
|
for (Integer time : times) {
|
||||||
Double current = fd.computePhiMeasure(time);
|
Double current = fd.computePhiMeasure(time);
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
Assertions.assertTrue(current < failureThreshold);
|
Assertions.assertTrue(current < failureThreshold);
|
||||||
}
|
}
|
||||||
fd.recordHeartbeat(time);
|
fd.recordHeartbeat(time);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TriConsumer<Integer, Integer, Integer> checkDeadMonotonic = (begin, end, step) -> {
|
TriConsumer<Integer, Integer, Integer> checkDeadMonotonic =
|
||||||
List<Integer> times = generateTimeList(begin, end, step);
|
(begin, end, step) -> {
|
||||||
Double prev = null;
|
List<Integer> times = generateTimeList(begin, end, step);
|
||||||
for (Integer time : times) {
|
Double prev = null;
|
||||||
|
for (Integer time : times) {
|
||||||
Double current = fd.computePhiMeasure(time);
|
Double current = fd.computePhiMeasure(time);
|
||||||
if (current != null && prev != null) {
|
if (current != null && prev != null) {
|
||||||
Assertions.assertTrue(current >= prev);
|
Assertions.assertTrue(current >= prev);
|
||||||
}
|
}
|
||||||
prev = current;
|
prev = current;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
checkAlive.accept(0, 20000, 100);
|
checkAlive.accept(0, 20000, 100);
|
||||||
checkDeadMonotonic.accept(20000, 20500, 5);
|
checkDeadMonotonic.accept(20000, 20500, 5);
|
||||||
|
@ -17,15 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.crdt;
|
package org.apache.gossip.crdt;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Abstract test suit to test CrdtSets with Add and Remove operations.
|
Abstract test suit to test CrdtSets with Add and Remove operations.
|
||||||
|
@ -23,29 +23,29 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class GrowOnlyCounterTest {
|
public class GrowOnlyCounterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest() {
|
public void mergeTest() {
|
||||||
|
|
||||||
Map<String, Long> node1Counter = new HashMap<>();
|
Map<String, Long> node1Counter = new HashMap<>();
|
||||||
node1Counter.put("1", 3L);
|
node1Counter.put("1", 3L);
|
||||||
Map<String, Long> node2Counter = new HashMap<>();
|
Map<String, Long> node2Counter = new HashMap<>();
|
||||||
node2Counter.put("2", 1L);
|
node2Counter.put("2", 1L);
|
||||||
Map<String, Long> node3Counter = new HashMap<>();
|
Map<String, Long> node3Counter = new HashMap<>();
|
||||||
node3Counter.put("3", 2L);
|
node3Counter.put("3", 2L);
|
||||||
|
|
||||||
GrowOnlyCounter gCounter1 = new GrowOnlyCounter(node1Counter);
|
GrowOnlyCounter gCounter1 = new GrowOnlyCounter(node1Counter);
|
||||||
GrowOnlyCounter gCounter2 = new GrowOnlyCounter(node2Counter);
|
GrowOnlyCounter gCounter2 = new GrowOnlyCounter(node2Counter);
|
||||||
GrowOnlyCounter gCounter3 = new GrowOnlyCounter(node3Counter);
|
GrowOnlyCounter gCounter3 = new GrowOnlyCounter(node3Counter);
|
||||||
|
|
||||||
// After node 2 receive from node 1
|
// After node 2 receive from node 1
|
||||||
gCounter2 = gCounter2.merge(gCounter1);
|
gCounter2 = gCounter2.merge(gCounter1);
|
||||||
Assert.assertEquals(4, (long) gCounter2.value());
|
Assert.assertEquals(4, (long) gCounter2.value());
|
||||||
|
|
||||||
// After node 3 receive from node 1
|
// After node 3 receive from node 1
|
||||||
gCounter3 = gCounter3.merge(gCounter1);
|
gCounter3 = gCounter3.merge(gCounter1);
|
||||||
Assert.assertEquals(5, (long) gCounter3.value());
|
Assert.assertEquals(5, (long) gCounter3.value());
|
||||||
|
|
||||||
// After node 3 receive from node 2
|
// After node 3 receive from node 2
|
||||||
gCounter3 = gCounter3.merge(gCounter2);
|
gCounter3 = gCounter3.merge(gCounter2);
|
||||||
Assert.assertEquals(6, (long) gCounter3.value());
|
Assert.assertEquals(6, (long) gCounter3.value());
|
||||||
|
@ -19,7 +19,6 @@ package org.apache.gossip.crdt;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -17,15 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.crdt;
|
package org.apache.gossip.crdt;
|
||||||
|
|
||||||
import org.apache.gossip.manager.Clock;
|
|
||||||
import org.apache.gossip.manager.SystemClock;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.apache.gossip.manager.Clock;
|
||||||
|
import org.apache.gossip.manager.SystemClock;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class LwwSetTest extends AddRemoveStringSetTest<LwwSet<String>> {
|
public class LwwSetTest extends AddRemoveStringSetTest<LwwSet<String>> {
|
||||||
private static Clock clock = new SystemClock();
|
private static Clock clock = new SystemClock();
|
||||||
|
@ -25,16 +25,16 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class MaxChangeSetTest extends AddRemoveStringSetTest<MaxChangeSet<String>> {
|
public class MaxChangeSetTest extends AddRemoveStringSetTest<MaxChangeSet<String>> {
|
||||||
MaxChangeSet<String> construct(Set<String> set){
|
MaxChangeSet<String> construct(Set<String> set) {
|
||||||
return new MaxChangeSet<>(set);
|
return new MaxChangeSet<>(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
MaxChangeSet<String> construct(){
|
MaxChangeSet<String> construct() {
|
||||||
return new MaxChangeSet<>();
|
return new MaxChangeSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void valueTest(){
|
public void valueTest() {
|
||||||
Map<Character, Integer> struct = new HashMap<>();
|
Map<Character, Integer> struct = new HashMap<>();
|
||||||
struct.put('a', 0);
|
struct.put('a', 0);
|
||||||
struct.put('b', 1);
|
struct.put('b', 1);
|
||||||
@ -47,7 +47,7 @@ public class MaxChangeSetTest extends AddRemoveStringSetTest<MaxChangeSet<String
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest(){
|
public void mergeTest() {
|
||||||
MaxChangeSet<Integer> set1 = new MaxChangeSet<Integer>().add(1); // Set with one operation on 1
|
MaxChangeSet<Integer> set1 = new MaxChangeSet<Integer>().add(1); // Set with one operation on 1
|
||||||
MaxChangeSet<Integer> set2 = new MaxChangeSet<Integer>().add(1).remove(1); // two operations
|
MaxChangeSet<Integer> set2 = new MaxChangeSet<Integer>().add(1).remove(1); // two operations
|
||||||
Assert.assertEquals(set1.merge(set2), new MaxChangeSet<Integer>()); // empty set wins
|
Assert.assertEquals(set1.merge(set2), new MaxChangeSet<Integer>()); // empty set wins
|
||||||
|
@ -25,22 +25,22 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
|
public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
|
||||||
OrSet<String> construct(){
|
OrSet<String> construct() {
|
||||||
return new OrSet<>();
|
return new OrSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
OrSet<String> construct(Set<String> set){
|
OrSet<String> construct(Set<String> set) {
|
||||||
return new OrSet<>(set);
|
return new OrSet<>(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void atest(){
|
public void atest() {
|
||||||
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(4).add(5).add(6).remove(5));
|
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(4).add(5).add(6).remove(5));
|
||||||
Assert.assertArrayEquals(Arrays.asList(4, 6).toArray(), i.value().toArray());
|
Assert.assertArrayEquals(Arrays.asList(4, 6).toArray(), i.value().toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest(){
|
public void mergeTest() {
|
||||||
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(4).add(5).add(6).remove(5));
|
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(4).add(5).add(6).remove(5));
|
||||||
Assert.assertArrayEquals(Arrays.asList(4, 6).toArray(), i.value().toArray());
|
Assert.assertArrayEquals(Arrays.asList(4, 6).toArray(), i.value().toArray());
|
||||||
OrSet<Integer> j = new OrSet<>(new OrSet.Builder<Integer>().add(9).add(4).add(5).remove(6));
|
OrSet<Integer> j = new OrSet<>(new OrSet.Builder<Integer>().add(9).add(4).add(5).remove(6));
|
||||||
@ -49,11 +49,11 @@ public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest2(){
|
public void mergeTest2() {
|
||||||
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(5).add(4).remove(4).add(6));
|
OrSet<Integer> i = new OrSet<>(new OrSet.Builder<Integer>().add(5).add(4).remove(4).add(6));
|
||||||
Assert.assertEquals(new OrSet<>(5, 6), i);
|
Assert.assertEquals(new OrSet<>(5, 6), i);
|
||||||
SortedSet<Integer> tree = new TreeSet<>();
|
SortedSet<Integer> tree = new TreeSet<>();
|
||||||
for (Integer in : i.value()){
|
for (Integer in : i.value()) {
|
||||||
tree.add(in);
|
tree.add(in);
|
||||||
}
|
}
|
||||||
TreeSet<Integer> compare = new TreeSet<>();
|
TreeSet<Integer> compare = new TreeSet<>();
|
||||||
@ -63,32 +63,32 @@ public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest4(){
|
public void mergeTest4() {
|
||||||
Assert.assertArrayEquals(new Integer[]{},
|
Assert.assertArrayEquals(
|
||||||
new OrSet<>(new OrSet.Builder<Integer>().add(1).remove(1)).toArray());
|
new Integer[] {}, new OrSet<>(new OrSet.Builder<Integer>().add(1).remove(1)).toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest3(){
|
public void mergeTest3() {
|
||||||
OrSet<Integer> i = new OrSet<>(1);
|
OrSet<Integer> i = new OrSet<>(1);
|
||||||
OrSet<Integer> j = new OrSet<>(2);
|
OrSet<Integer> j = new OrSet<>(2);
|
||||||
OrSet<Integer> k = new OrSet<>(i.merge(j), new OrSet.Builder<Integer>().remove(1));
|
OrSet<Integer> k = new OrSet<>(i.merge(j), new OrSet.Builder<Integer>().remove(1));
|
||||||
Assert.assertArrayEquals(new Integer[]{2}, i.merge(j).merge(k).toArray());
|
Assert.assertArrayEquals(new Integer[] {2}, i.merge(j).merge(k).toArray());
|
||||||
Assert.assertArrayEquals(new Integer[]{2}, j.merge(i).merge(k).toArray());
|
Assert.assertArrayEquals(new Integer[] {2}, j.merge(i).merge(k).toArray());
|
||||||
Assert.assertArrayEquals(new Integer[]{2}, k.merge(i).merge(j).toArray());
|
Assert.assertArrayEquals(new Integer[] {2}, k.merge(i).merge(j).toArray());
|
||||||
Assert.assertArrayEquals(new Integer[]{2}, k.merge(j).merge(i).toArray());
|
Assert.assertArrayEquals(new Integer[] {2}, k.merge(j).merge(i).toArray());
|
||||||
Assert.assertEquals(j, i.merge(j.merge(k)));
|
Assert.assertEquals(j, i.merge(j.merge(k)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTest9(){
|
public void mergeTest9() {
|
||||||
OrSet<Integer> i = new OrSet<>(19);
|
OrSet<Integer> i = new OrSet<>(19);
|
||||||
OrSet<Integer> j = i.merge(i);
|
OrSet<Integer> j = i.merge(i);
|
||||||
Assert.assertEquals(i.value(), j.value());
|
Assert.assertEquals(i.value(), j.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTestSame(){
|
public void mergeTestSame() {
|
||||||
OrSet<Integer> i = new OrSet<>(19);
|
OrSet<Integer> i = new OrSet<>(19);
|
||||||
OrSet<Integer> j = new OrSet<>(19);
|
OrSet<Integer> j = new OrSet<>(19);
|
||||||
OrSet<Integer> k = i.merge(j);
|
OrSet<Integer> k = i.merge(j);
|
||||||
@ -98,4 +98,4 @@ public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
|
|||||||
Assert.assertEquals(2, y.getElements().get(19).size());
|
Assert.assertEquals(2, y.getElements().get(19).size());
|
||||||
Assert.assertEquals(new OrSet<Integer>().value(), y.value());
|
Assert.assertEquals(new OrSet<Integer>().value(), y.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.gossip.LocalMember;
|
import org.apache.gossip.LocalMember;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -17,13 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.crdt;
|
package org.apache.gossip.crdt;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TwoPhaseSetTest {
|
public class TwoPhaseSetTest {
|
||||||
|
|
||||||
|
@ -18,13 +18,12 @@
|
|||||||
package org.apache.gossip.event.data;
|
package org.apache.gossip.event.data;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class DataEventManagerTest {
|
public class DataEventManagerTest {
|
||||||
|
|
||||||
private static Semaphore semaphore;
|
private static Semaphore semaphore;
|
||||||
|
@ -17,12 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.lock.vote;
|
package org.apache.gossip.lock.vote;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class MajorityVoteTest {
|
public class MajorityVoteTest {
|
||||||
|
|
||||||
|
@ -18,16 +18,14 @@
|
|||||||
package org.apache.gossip.manager;
|
package org.apache.gossip.manager;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
import io.teknek.tunit.TUnit;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import org.apache.gossip.GossipSettings;
|
import org.apache.gossip.GossipSettings;
|
||||||
import org.apache.gossip.model.PerNodeDataMessage;
|
import org.apache.gossip.model.PerNodeDataMessage;
|
||||||
import org.apache.gossip.model.SharedDataMessage;
|
import org.apache.gossip.model.SharedDataMessage;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
|
||||||
|
|
||||||
public class DataReaperTest {
|
public class DataReaperTest {
|
||||||
|
|
||||||
private final MetricRegistry registry = new MetricRegistry();
|
private final MetricRegistry registry = new MetricRegistry();
|
||||||
|
@ -22,7 +22,6 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.apache.gossip.GossipSettings;
|
import org.apache.gossip.GossipSettings;
|
||||||
import org.apache.gossip.model.PerNodeDataMessage;
|
import org.apache.gossip.model.PerNodeDataMessage;
|
||||||
import org.apache.gossip.model.SharedDataMessage;
|
import org.apache.gossip.model.SharedDataMessage;
|
||||||
|
@ -28,7 +28,8 @@ import org.junit.Test;
|
|||||||
public class MessageHandlerTest {
|
public class MessageHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleHandler() {
|
public void testSimpleHandler() {
|
||||||
MessageHandler mi = new TypedMessageHandlerWrapper(FakeMessage.class, new DummyMessageHandler());
|
MessageHandler mi =
|
||||||
|
new TypedMessageHandlerWrapper(FakeMessage.class, new DummyMessageHandler());
|
||||||
Assert.assertTrue(mi.invoke(null, null, new FakeMessage()));
|
Assert.assertTrue(mi.invoke(null, null, new FakeMessage()));
|
||||||
Assert.assertFalse(mi.invoke(null, null, new ActiveGossipMessage()));
|
Assert.assertFalse(mi.invoke(null, null, new ActiveGossipMessage()));
|
||||||
}
|
}
|
||||||
@ -72,7 +73,8 @@ public class MessageHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMessageHandlerCombiner() {
|
public void testMessageHandlerCombiner() {
|
||||||
// Empty combiner - false result
|
// Empty combiner - false result
|
||||||
MessageHandler mi = MessageHandlerFactory.concurrentHandler((gossipCore, gossipManager, base) -> false);
|
MessageHandler mi =
|
||||||
|
MessageHandlerFactory.concurrentHandler((gossipCore, gossipManager, base) -> false);
|
||||||
Assert.assertFalse(mi.invoke(null, null, new Base()));
|
Assert.assertFalse(mi.invoke(null, null, new Base()));
|
||||||
|
|
||||||
DummyMessageHandler h = new DummyMessageHandler();
|
DummyMessageHandler h = new DummyMessageHandler();
|
||||||
@ -86,7 +88,9 @@ public class MessageHandlerTest {
|
|||||||
Assert.assertEquals(2, h.counter);
|
Assert.assertEquals(2, h.counter);
|
||||||
|
|
||||||
// Increase size in runtime. Should be 3 calls: 2+3 = 5
|
// Increase size in runtime. Should be 3 calls: 2+3 = 5
|
||||||
mi = MessageHandlerFactory.concurrentHandler(mi, new TypedMessageHandlerWrapper(FakeMessage.class, h));
|
mi =
|
||||||
|
MessageHandlerFactory.concurrentHandler(
|
||||||
|
mi, new TypedMessageHandlerWrapper(FakeMessage.class, h));
|
||||||
Assert.assertTrue(mi.invoke(null, null, new FakeMessage()));
|
Assert.assertTrue(mi.invoke(null, null, new FakeMessage()));
|
||||||
Assert.assertEquals(5, h.counter);
|
Assert.assertEquals(5, h.counter);
|
||||||
}
|
}
|
||||||
|
@ -29,21 +29,23 @@ import org.apache.gossip.model.Base;
|
|||||||
|
|
||||||
// doesn't serialize anything besides longs. Uses a static lookup table to read and write objects.
|
// doesn't serialize anything besides longs. Uses a static lookup table to read and write objects.
|
||||||
public class UnitTestProtocolManager implements ProtocolManager {
|
public class UnitTestProtocolManager implements ProtocolManager {
|
||||||
|
|
||||||
// so it can be shared across gossipers. this works as long as each object has a different memory address.
|
// so it can be shared across gossipers. this works as long as each object has a different memory
|
||||||
|
// address.
|
||||||
private static final Map<Long, Base> lookup = new ConcurrentHashMap<>();
|
private static final Map<Long, Base> lookup = new ConcurrentHashMap<>();
|
||||||
private final Meter meter;
|
private final Meter meter;
|
||||||
|
|
||||||
public UnitTestProtocolManager(GossipSettings settings, String id, MetricRegistry registry) {
|
public UnitTestProtocolManager(GossipSettings settings, String id, MetricRegistry registry) {
|
||||||
meter = settings.isSignMessages() ?
|
meter =
|
||||||
registry.meter(PassiveGossipConstants.SIGNED_MESSAGE) :
|
settings.isSignMessages()
|
||||||
registry.meter(PassiveGossipConstants.UNSIGNED_MESSAGE);
|
? registry.meter(PassiveGossipConstants.SIGNED_MESSAGE)
|
||||||
|
: registry.meter(PassiveGossipConstants.UNSIGNED_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] longToBytes(long val) {
|
private static byte[] longToBytes(long val) {
|
||||||
byte[] b = new byte[8];
|
byte[] b = new byte[8];
|
||||||
b[7] = (byte) (val);
|
b[7] = (byte) (val);
|
||||||
b[6] = (byte) (val >>> 8);
|
b[6] = (byte) (val >>> 8);
|
||||||
b[5] = (byte) (val >>> 16);
|
b[5] = (byte) (val >>> 16);
|
||||||
b[4] = (byte) (val >>> 24);
|
b[4] = (byte) (val >>> 24);
|
||||||
b[3] = (byte) (val >>> 32);
|
b[3] = (byte) (val >>> 32);
|
||||||
@ -54,16 +56,16 @@ public class UnitTestProtocolManager implements ProtocolManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long bytesToLong(byte[] b) {
|
static long bytesToLong(byte[] b) {
|
||||||
return ((b[7] & 0xFFL)) +
|
return ((b[7] & 0xFFL))
|
||||||
((b[6] & 0xFFL) << 8) +
|
+ ((b[6] & 0xFFL) << 8)
|
||||||
((b[5] & 0xFFL) << 16) +
|
+ ((b[5] & 0xFFL) << 16)
|
||||||
((b[4] & 0xFFL) << 24) +
|
+ ((b[4] & 0xFFL) << 24)
|
||||||
((b[3] & 0xFFL) << 32) +
|
+ ((b[3] & 0xFFL) << 32)
|
||||||
((b[2] & 0xFFL) << 40) +
|
+ ((b[2] & 0xFFL) << 40)
|
||||||
((b[1] & 0xFFL) << 48) +
|
+ ((b[1] & 0xFFL) << 48)
|
||||||
(((long) b[0]) << 56);
|
+ (((long) b[0]) << 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] write(Base message) throws IOException {
|
public byte[] write(Base message) throws IOException {
|
||||||
long hashCode = System.identityHashCode(message);
|
long hashCode = System.identityHashCode(message);
|
||||||
|
@ -30,9 +30,9 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class DataReplicationTest {
|
public class DataReplicationTest {
|
||||||
|
|
||||||
private static SharedDataMessage getSharedNodeData(String key, String value,
|
private static SharedDataMessage getSharedNodeData(
|
||||||
Replicable<SharedDataMessage> replicable) {
|
String key, String value, Replicable<SharedDataMessage> replicable) {
|
||||||
SharedDataMessage g = new SharedDataMessage();
|
SharedDataMessage g = new SharedDataMessage();
|
||||||
g.setExpireAt(Long.MAX_VALUE);
|
g.setExpireAt(Long.MAX_VALUE);
|
||||||
g.setKey(key);
|
g.setKey(key);
|
||||||
@ -41,114 +41,118 @@ public class DataReplicationTest {
|
|||||||
g.setReplicable(replicable);
|
g.setReplicable(replicable);
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LocalMember getLocalMember(URI uri, String id){
|
private static LocalMember getLocalMember(URI uri, String id) {
|
||||||
return new LocalMember("cluster1", uri, id, 0, null, 1, 0, "");
|
return new LocalMember("cluster1", uri, id, 0, null, 1, 0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LocalMember getLocalMemberDc(URI uri, String id, String dataCenter, String rack){
|
private static LocalMember getLocalMemberDc(URI uri, String id, String dataCenter, String rack) {
|
||||||
Map<String, String> props = new HashMap<>();
|
Map<String, String> props = new HashMap<>();
|
||||||
props.put(DatacenterRackAwareActiveGossiper.DATACENTER, dataCenter);
|
props.put(DatacenterRackAwareActiveGossiper.DATACENTER, dataCenter);
|
||||||
props.put(DatacenterRackAwareActiveGossiper.RACK, rack);
|
props.put(DatacenterRackAwareActiveGossiper.RACK, rack);
|
||||||
return new LocalMember("cluster1", uri, id, 0, props, 1, 0, "");
|
return new LocalMember("cluster1", uri, id, 0, props, 1, 0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateAllTest() throws URISyntaxException {
|
public void dataReplicateAllTest() throws URISyntaxException {
|
||||||
SharedDataMessage message = getSharedNodeData("public","public", new AllReplicable<>());
|
SharedDataMessage message = getSharedNodeData("public", "public", new AllReplicable<>());
|
||||||
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8001"),"1");
|
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8001"), "1");
|
||||||
LocalMember member = getLocalMember(new URI("udp://127.0.0.1:8002"),"2");
|
LocalMember member = getLocalMember(new URI("udp://127.0.0.1:8002"), "2");
|
||||||
Assert.assertEquals(true, message.getReplicable().shouldReplicate(me, member, message));
|
Assert.assertEquals(true, message.getReplicable().shouldReplicate(me, member, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateNoneTest() throws URISyntaxException {
|
public void dataReplicateNoneTest() throws URISyntaxException {
|
||||||
SharedDataMessage message = getSharedNodeData("private","private", new NotReplicable<>());
|
SharedDataMessage message = getSharedNodeData("private", "private", new NotReplicable<>());
|
||||||
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8001"),"1");
|
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8001"), "1");
|
||||||
LocalMember member = getLocalMember(new URI("udp://127.0.0.1:8002"),"2");
|
LocalMember member = getLocalMember(new URI("udp://127.0.0.1:8002"), "2");
|
||||||
Assert.assertEquals(false, message.getReplicable().shouldReplicate(me, member, message));
|
Assert.assertEquals(false, message.getReplicable().shouldReplicate(me, member, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateWhiteListTest() throws URISyntaxException {
|
public void dataReplicateWhiteListTest() throws URISyntaxException {
|
||||||
List<LocalMember> memberList = new ArrayList<>();
|
List<LocalMember> memberList = new ArrayList<>();
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"),"1"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"), "1"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"),"2"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"), "2"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8003"),"3"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8003"), "3"));
|
||||||
// add node 1 and 2 to the white list
|
// add node 1 and 2 to the white list
|
||||||
List<LocalMember> whiteList = new ArrayList<>();
|
List<LocalMember> whiteList = new ArrayList<>();
|
||||||
whiteList.add(memberList.get(0));
|
whiteList.add(memberList.get(0));
|
||||||
whiteList.add(memberList.get(1));
|
whiteList.add(memberList.get(1));
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("whiteList", "Only allow some nodes",
|
SharedDataMessage message =
|
||||||
new WhiteListReplicable<>(whiteList));
|
getSharedNodeData(
|
||||||
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8004"),"4");
|
"whiteList", "Only allow some nodes", new WhiteListReplicable<>(whiteList));
|
||||||
|
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8004"), "4");
|
||||||
|
|
||||||
// data should replicate to node 1 and 2 but not 3
|
// data should replicate to node 1 and 2 but not 3
|
||||||
Assert.assertEquals(true,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(0), message));
|
true, message.getReplicable().shouldReplicate(me, memberList.get(0), message));
|
||||||
Assert.assertEquals(true,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(1), message));
|
true, message.getReplicable().shouldReplicate(me, memberList.get(1), message));
|
||||||
Assert.assertEquals(false,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(2), message));
|
false, message.getReplicable().shouldReplicate(me, memberList.get(2), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateWhiteListNullTest() throws URISyntaxException {
|
public void dataReplicateWhiteListNullTest() throws URISyntaxException {
|
||||||
List<LocalMember> memberList = new ArrayList<>();
|
List<LocalMember> memberList = new ArrayList<>();
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"),"1"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"), "1"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"),"2"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"), "2"));
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("whiteList", "Only allow some nodes",
|
SharedDataMessage message =
|
||||||
new WhiteListReplicable<>(null));
|
getSharedNodeData("whiteList", "Only allow some nodes", new WhiteListReplicable<>(null));
|
||||||
|
|
||||||
// data should not replicate if no whitelist specified
|
// data should not replicate if no whitelist specified
|
||||||
Assert.assertEquals(false,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(memberList.get(0), memberList.get(1), message));
|
false,
|
||||||
Assert.assertEquals(false,
|
message.getReplicable().shouldReplicate(memberList.get(0), memberList.get(1), message));
|
||||||
message.getReplicable().shouldReplicate(memberList.get(1), memberList.get(0), message));
|
Assert.assertEquals(
|
||||||
|
false,
|
||||||
|
message.getReplicable().shouldReplicate(memberList.get(1), memberList.get(0), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateBlackListTest() throws URISyntaxException {
|
public void dataReplicateBlackListTest() throws URISyntaxException {
|
||||||
List<LocalMember> memberList = new ArrayList<>();
|
List<LocalMember> memberList = new ArrayList<>();
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"),"1"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"), "1"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"),"2"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"), "2"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8003"),"3"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8003"), "3"));
|
||||||
// add node 1 and 2 to the black list
|
// add node 1 and 2 to the black list
|
||||||
List<LocalMember> blackList = new ArrayList<>();
|
List<LocalMember> blackList = new ArrayList<>();
|
||||||
blackList.add(memberList.get(0));
|
blackList.add(memberList.get(0));
|
||||||
blackList.add(memberList.get(1));
|
blackList.add(memberList.get(1));
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("blackList", "Disallow some nodes",
|
SharedDataMessage message =
|
||||||
new BlackListReplicable<>(blackList));
|
getSharedNodeData("blackList", "Disallow some nodes", new BlackListReplicable<>(blackList));
|
||||||
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8004"),"4");
|
LocalMember me = getLocalMember(new URI("udp://127.0.0.1:8004"), "4");
|
||||||
|
|
||||||
// data should not replicate to node 1 and 2
|
// data should not replicate to node 1 and 2
|
||||||
Assert.assertEquals(false,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(0), message));
|
false, message.getReplicable().shouldReplicate(me, memberList.get(0), message));
|
||||||
Assert.assertEquals(false,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(1), message));
|
false, message.getReplicable().shouldReplicate(me, memberList.get(1), message));
|
||||||
Assert.assertEquals(true,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(me, memberList.get(2), message));
|
true, message.getReplicable().shouldReplicate(me, memberList.get(2), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataReplicateBlackListNullTest() throws URISyntaxException {
|
public void dataReplicateBlackListNullTest() throws URISyntaxException {
|
||||||
|
|
||||||
List<LocalMember> memberList = new ArrayList<>();
|
List<LocalMember> memberList = new ArrayList<>();
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"),"1"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8001"), "1"));
|
||||||
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"),"2"));
|
memberList.add(getLocalMember(new URI("udp://127.0.0.1:8002"), "2"));
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("blackList", "Disallow some nodes",
|
SharedDataMessage message =
|
||||||
new BlackListReplicable<>(null));
|
getSharedNodeData("blackList", "Disallow some nodes", new BlackListReplicable<>(null));
|
||||||
|
|
||||||
// data should replicate if no blacklist specified
|
// data should replicate if no blacklist specified
|
||||||
Assert.assertEquals(true,
|
Assert.assertEquals(
|
||||||
message.getReplicable().shouldReplicate(memberList.get(0), memberList.get(1), message));
|
true,
|
||||||
Assert.assertEquals(true,
|
message.getReplicable().shouldReplicate(memberList.get(0), memberList.get(1), message));
|
||||||
message.getReplicable().shouldReplicate(memberList.get(1), memberList.get(0), message));
|
Assert.assertEquals(
|
||||||
|
true,
|
||||||
|
message.getReplicable().shouldReplicate(memberList.get(1), memberList.get(0), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -157,28 +161,41 @@ public class DataReplicationTest {
|
|||||||
List<LocalMember> memberListDc1 = new ArrayList<>();
|
List<LocalMember> memberListDc1 = new ArrayList<>();
|
||||||
List<LocalMember> memberListDc2 = new ArrayList<>();
|
List<LocalMember> memberListDc2 = new ArrayList<>();
|
||||||
|
|
||||||
memberListDc1
|
memberListDc1.add(
|
||||||
.add(getLocalMemberDc(new URI("udp://10.0.0.1:8000"), "1", "DataCenter1", "Rack1"));
|
getLocalMemberDc(new URI("udp://10.0.0.1:8000"), "1", "DataCenter1", "Rack1"));
|
||||||
memberListDc1
|
memberListDc1.add(
|
||||||
.add(getLocalMemberDc(new URI("udp://10.0.0.2:8000"), "2", "DataCenter1", "Rack2"));
|
getLocalMemberDc(new URI("udp://10.0.0.2:8000"), "2", "DataCenter1", "Rack2"));
|
||||||
memberListDc2
|
memberListDc2.add(
|
||||||
.add(getLocalMemberDc(new URI("udp://10.0.1.1:8000"), "11", "DataCenter2", "Rack1"));
|
getLocalMemberDc(new URI("udp://10.0.1.1:8000"), "11", "DataCenter2", "Rack1"));
|
||||||
memberListDc2
|
memberListDc2.add(
|
||||||
.add(getLocalMemberDc(new URI("udp://10.0.1.2:8000"), "12", "DataCenter2", "Rack2"));
|
getLocalMemberDc(new URI("udp://10.0.1.2:8000"), "12", "DataCenter2", "Rack2"));
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("datacenter1", "I am in data center 1 rack 1",
|
SharedDataMessage message =
|
||||||
new DataCenterReplicable<>());
|
getSharedNodeData(
|
||||||
|
"datacenter1", "I am in data center 1 rack 1", new DataCenterReplicable<>());
|
||||||
|
|
||||||
// data should replicate in data center 1
|
// data should replicate in data center 1
|
||||||
Assert.assertEquals(true, message.getReplicable()
|
Assert.assertEquals(
|
||||||
|
true,
|
||||||
|
message
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(memberListDc1.get(0), memberListDc1.get(1), message));
|
.shouldReplicate(memberListDc1.get(0), memberListDc1.get(1), message));
|
||||||
Assert.assertEquals(true, message.getReplicable()
|
Assert.assertEquals(
|
||||||
|
true,
|
||||||
|
message
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(memberListDc2.get(0), memberListDc2.get(1), message));
|
.shouldReplicate(memberListDc2.get(0), memberListDc2.get(1), message));
|
||||||
|
|
||||||
// data should not replicate to data center 2
|
// data should not replicate to data center 2
|
||||||
Assert.assertEquals(false, message.getReplicable()
|
Assert.assertEquals(
|
||||||
|
false,
|
||||||
|
message
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(memberListDc1.get(0), memberListDc2.get(0), message));
|
.shouldReplicate(memberListDc1.get(0), memberListDc2.get(0), message));
|
||||||
Assert.assertEquals(false, message.getReplicable()
|
Assert.assertEquals(
|
||||||
|
false,
|
||||||
|
message
|
||||||
|
.getReplicable()
|
||||||
.shouldReplicate(memberListDc1.get(1), memberListDc2.get(1), message));
|
.shouldReplicate(memberListDc1.get(1), memberListDc2.get(1), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,21 +203,22 @@ public class DataReplicationTest {
|
|||||||
public void dataReplicateDataCenterUnknownDataCenterTest() throws URISyntaxException {
|
public void dataReplicateDataCenterUnknownDataCenterTest() throws URISyntaxException {
|
||||||
|
|
||||||
List<LocalMember> memberListDc1 = new ArrayList<>();
|
List<LocalMember> memberListDc1 = new ArrayList<>();
|
||||||
memberListDc1
|
memberListDc1.add(
|
||||||
.add(getLocalMemberDc(new URI("udp://10.0.0.1:8000"), "1", "DataCenter1", "Rack1"));
|
getLocalMemberDc(new URI("udp://10.0.0.1:8000"), "1", "DataCenter1", "Rack1"));
|
||||||
|
|
||||||
Map<String, String> properties = new HashMap<>();
|
Map<String, String> properties = new HashMap<>();
|
||||||
LocalMember unknownDc = new LocalMember("cluster1", new URI("udp://10.0.1.2:8000"), "12", 0,
|
LocalMember unknownDc =
|
||||||
properties, 1, 0, "");
|
new LocalMember("cluster1", new URI("udp://10.0.1.2:8000"), "12", 0, properties, 1, 0, "");
|
||||||
|
|
||||||
SharedDataMessage message = getSharedNodeData("datacenter1","I am in data center 1 rack 1", new DataCenterReplicable<>());
|
SharedDataMessage message =
|
||||||
|
getSharedNodeData(
|
||||||
|
"datacenter1", "I am in data center 1 rack 1", new DataCenterReplicable<>());
|
||||||
|
|
||||||
// data should not replicate from dc1 to unknown node
|
// data should not replicate from dc1 to unknown node
|
||||||
Assert.assertEquals(false, message.getReplicable()
|
Assert.assertEquals(
|
||||||
.shouldReplicate(memberListDc1.get(0), unknownDc, message));
|
false, message.getReplicable().shouldReplicate(memberListDc1.get(0), unknownDc, message));
|
||||||
// data can replicate from unknown node to dc
|
// data can replicate from unknown node to dc
|
||||||
Assert.assertEquals(true, message.getReplicable()
|
Assert.assertEquals(
|
||||||
.shouldReplicate(unknownDc, memberListDc1.get(0), message));
|
true, message.getReplicable().shouldReplicate(unknownDc, memberListDc1.get(0), message));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,14 @@
|
|||||||
|
|
||||||
package org.apache.gossip.transport;
|
package org.apache.gossip.transport;
|
||||||
|
|
||||||
import org.apache.gossip.manager.GossipCore;
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.apache.gossip.manager.GossipCore;
|
||||||
|
import org.apache.gossip.manager.GossipManager;
|
||||||
|
|
||||||
/** Only use in unit tests! */
|
/** Only use in unit tests! */
|
||||||
public class UnitTestTransportManager extends AbstractTransportManager {
|
public class UnitTestTransportManager extends AbstractTransportManager {
|
||||||
|
@ -21,10 +21,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class RunStandardExamples {
|
public class RunStandardExamples {
|
||||||
|
|
||||||
private static boolean WILL_READ = true;
|
|
||||||
|
|
||||||
private static boolean WILL_NOT_READ = false;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
if ((args.length < 1) || args[0].equals("-h") || args[0].equals("--help") || args.length < 2) {
|
if ((args.length < 1) || args[0].equals("-h") || args[0].equals("--help") || args.length < 2) {
|
||||||
System.out.print(usage());
|
System.out.print(usage());
|
||||||
@ -37,16 +33,18 @@ public class RunStandardExamples {
|
|||||||
System.out.print(usage());
|
System.out.print(usage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runExaple(example, channel);
|
runExample(example, channel);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.print(usage());
|
System.out.print(usage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runExaple(int exampleNumber, int channel) throws IOException {
|
private static void runExample(int exampleNumber, int channel) throws IOException {
|
||||||
String[] args = stanardArgs(channel, new String[4]);
|
String[] args = standardArgs(channel, new String[4]);
|
||||||
|
boolean WILL_READ = true;
|
||||||
if (exampleNumber == 1) {
|
if (exampleNumber == 1) {
|
||||||
StandAloneNode example = new StandAloneNode(args);
|
StandAloneNode example = new StandAloneNode(args);
|
||||||
|
boolean WILL_NOT_READ = false;
|
||||||
example.exec(WILL_NOT_READ);
|
example.exec(WILL_NOT_READ);
|
||||||
} else if (exampleNumber == 2) {
|
} else if (exampleNumber == 2) {
|
||||||
StandAloneNodeCrdtOrSet example = new StandAloneNodeCrdtOrSet(args);
|
StandAloneNodeCrdtOrSet example = new StandAloneNodeCrdtOrSet(args);
|
||||||
@ -61,7 +59,7 @@ public class RunStandardExamples {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] stanardArgs(int channel, String[] args) {
|
private static String[] standardArgs(int channel, String[] args) {
|
||||||
// see README.md for examples
|
// see README.md for examples
|
||||||
args[0] = "udp://localhost:1000" + channel;
|
args[0] = "udp://localhost:1000" + channel;
|
||||||
args[1] = "" + channel;
|
args[1] = "" + channel;
|
||||||
@ -71,7 +69,7 @@ public class RunStandardExamples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String[] extendedArgs(int channel, String[] args) {
|
private static String[] extendedArgs(int channel, String[] args) {
|
||||||
args = stanardArgs(channel, args);
|
standardArgs(channel, args);
|
||||||
// see README.md for examples
|
// see README.md for examples
|
||||||
if (channel == 0) {
|
if (channel == 0) {
|
||||||
args[4] = "1";
|
args[4] = "1";
|
||||||
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.gossip.GossipSettings;
|
import org.apache.gossip.GossipSettings;
|
||||||
@ -37,7 +38,7 @@ public class StandAloneDatacenterAndRack extends StandAloneExampleBase {
|
|||||||
initGossipManager(args);
|
initGossipManager(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException, IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
StandAloneDatacenterAndRack example = new StandAloneDatacenterAndRack(args);
|
StandAloneDatacenterAndRack example = new StandAloneDatacenterAndRack(args);
|
||||||
boolean willRead = true;
|
boolean willRead = true;
|
||||||
example.exec(willRead);
|
example.exec(willRead);
|
||||||
@ -61,8 +62,7 @@ public class StandAloneDatacenterAndRack extends StandAloneExampleBase {
|
|||||||
.uri(URI.create(args[0]))
|
.uri(URI.create(args[0]))
|
||||||
.id(args[1])
|
.id(args[1])
|
||||||
.gossipSettings(s)
|
.gossipSettings(s)
|
||||||
.gossipMembers(
|
.gossipMembers(List.of(new RemoteMember("mycluster", URI.create(args[2]), args[3])))
|
||||||
Arrays.asList(new RemoteMember("mycluster", URI.create(args[2]), args[3])))
|
|
||||||
.properties(props)
|
.properties(props)
|
||||||
.build();
|
.build();
|
||||||
manager.init();
|
manager.init();
|
||||||
|
@ -23,7 +23,6 @@ import java.io.InputStreamReader;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.gossip.GossipSettings;
|
import org.apache.gossip.GossipSettings;
|
||||||
import org.apache.gossip.LocalMember;
|
import org.apache.gossip.LocalMember;
|
||||||
import org.apache.gossip.RemoteMember;
|
import org.apache.gossip.RemoteMember;
|
||||||
|
@ -22,20 +22,17 @@ import org.apache.gossip.manager.GossipManager;
|
|||||||
|
|
||||||
public class StandAloneNode extends StandAloneExampleBase {
|
public class StandAloneNode extends StandAloneExampleBase {
|
||||||
|
|
||||||
private static boolean WILL_READ = false;
|
|
||||||
|
|
||||||
StandAloneNode(String[] args) {
|
StandAloneNode(String[] args) {
|
||||||
args = super.checkArgsForClearFlag(args);
|
args = super.checkArgsForClearFlag(args);
|
||||||
super.initGossipManager(args);
|
super.initGossipManager(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException, IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
StandAloneNode example = new StandAloneNode(args);
|
StandAloneNode example = new StandAloneNode(args);
|
||||||
|
boolean WILL_READ = false;
|
||||||
example.exec(WILL_READ);
|
example.exec(WILL_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void printValues(GossipManager gossipService) {
|
void printValues(GossipManager gossipService) {}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package org.apache.gossip.examples;
|
package org.apache.gossip.examples;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.gossip.crdt.GrowOnlyCounter;
|
import org.apache.gossip.crdt.GrowOnlyCounter;
|
||||||
import org.apache.gossip.crdt.OrSet;
|
import org.apache.gossip.crdt.OrSet;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
@ -35,7 +34,7 @@ public class StandAloneNodeCrdtOrSet extends StandAloneExampleBase {
|
|||||||
super.initGossipManager(args);
|
super.initGossipManager(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException, IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
StandAloneNodeCrdtOrSet example = new StandAloneNodeCrdtOrSet(args);
|
StandAloneNodeCrdtOrSet example = new StandAloneNodeCrdtOrSet(args);
|
||||||
boolean willRead = true;
|
boolean willRead = true;
|
||||||
example.exec(willRead);
|
example.exec(willRead);
|
||||||
@ -53,7 +52,7 @@ public class StandAloneNodeCrdtOrSet extends StandAloneExampleBase {
|
|||||||
|
|
||||||
private static void gcount(String val, GossipManager gossipManager) {
|
private static void gcount(String val, GossipManager gossipManager) {
|
||||||
GrowOnlyCounter c = (GrowOnlyCounter) gossipManager.findCrdt(INDEX_KEY_FOR_COUNTER);
|
GrowOnlyCounter c = (GrowOnlyCounter) gossipManager.findCrdt(INDEX_KEY_FOR_COUNTER);
|
||||||
Long l = Long.valueOf(val);
|
long l = Long.parseLong(val);
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
c = new GrowOnlyCounter(new GrowOnlyCounter.Builder(gossipManager).increment((l)));
|
c = new GrowOnlyCounter(new GrowOnlyCounter.Builder(gossipManager).increment((l)));
|
||||||
} else {
|
} else {
|
||||||
@ -73,7 +72,7 @@ public class StandAloneNodeCrdtOrSet extends StandAloneExampleBase {
|
|||||||
SharedDataMessage m = new SharedDataMessage();
|
SharedDataMessage m = new SharedDataMessage();
|
||||||
m.setExpireAt(Long.MAX_VALUE);
|
m.setExpireAt(Long.MAX_VALUE);
|
||||||
m.setKey(INDEX_KEY_FOR_SET);
|
m.setKey(INDEX_KEY_FOR_SET);
|
||||||
m.setPayload(new OrSet<String>(s, new OrSet.Builder<String>().remove(val)));
|
m.setPayload(new OrSet<>(s, new OrSet.Builder<String>().remove(val)));
|
||||||
m.setTimestamp(System.currentTimeMillis());
|
m.setTimestamp(System.currentTimeMillis());
|
||||||
gossipService.merge(m);
|
gossipService.merge(m);
|
||||||
}
|
}
|
||||||
@ -82,7 +81,7 @@ public class StandAloneNodeCrdtOrSet extends StandAloneExampleBase {
|
|||||||
SharedDataMessage m = new SharedDataMessage();
|
SharedDataMessage m = new SharedDataMessage();
|
||||||
m.setExpireAt(Long.MAX_VALUE);
|
m.setExpireAt(Long.MAX_VALUE);
|
||||||
m.setKey(INDEX_KEY_FOR_SET);
|
m.setKey(INDEX_KEY_FOR_SET);
|
||||||
m.setPayload(new OrSet<String>(val));
|
m.setPayload(new OrSet<>(val));
|
||||||
m.setTimestamp(System.currentTimeMillis());
|
m.setTimestamp(System.currentTimeMillis());
|
||||||
gossipService.merge(m);
|
gossipService.merge(m);
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ public class StandAloneNodeCrdtOrSet extends StandAloneExampleBase {
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
} else if (op == 'l') {
|
} else if (op == 'l') {
|
||||||
if ((val == INDEX_KEY_FOR_SET) || (val == INDEX_KEY_FOR_COUNTER)) {
|
if ((val.equals(INDEX_KEY_FOR_SET)) || (val.equals(INDEX_KEY_FOR_COUNTER))) {
|
||||||
listen(val, getGossipManager());
|
listen(val, getGossipManager());
|
||||||
} else {
|
} else {
|
||||||
valid = false;
|
valid = false;
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package org.apache.gossip.examples;
|
package org.apache.gossip.examples;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.gossip.crdt.PNCounter;
|
import org.apache.gossip.crdt.PNCounter;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.model.SharedDataMessage;
|
import org.apache.gossip.model.SharedDataMessage;
|
||||||
@ -30,7 +29,7 @@ public class StandAlonePNCounter extends StandAloneExampleBase {
|
|||||||
super.initGossipManager(args);
|
super.initGossipManager(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException, IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
StandAlonePNCounter example = new StandAlonePNCounter(args);
|
StandAlonePNCounter example = new StandAlonePNCounter(args);
|
||||||
boolean willRead = true;
|
boolean willRead = true;
|
||||||
example.exec(willRead);
|
example.exec(willRead);
|
||||||
@ -61,7 +60,7 @@ public class StandAlonePNCounter extends StandAloneExampleBase {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
if (op == 'i') {
|
if (op == 'i') {
|
||||||
increment(l, getGossipManager());
|
increment(l, getGossipManager());
|
||||||
} else if (op == 'd') {
|
} else {
|
||||||
decrement(l, getGossipManager());
|
decrement(l, getGossipManager());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,8 @@ public class DataTest {
|
|||||||
private final String pnCounterKey = "crdtpn";
|
private final String pnCounterKey = "crdtpn";
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeMembers() throws InterruptedException, UnknownHostException, URISyntaxException{
|
public static void initializeMembers()
|
||||||
|
throws InterruptedException, UnknownHostException, URISyntaxException {
|
||||||
final int clusterMembers = 2;
|
final int clusterMembers = 2;
|
||||||
|
|
||||||
GossipSettings settings = new GossipSettings();
|
GossipSettings settings = new GossipSettings();
|
||||||
@ -60,107 +61,122 @@ public class DataTest {
|
|||||||
settings.setPersistDataState(false);
|
settings.setPersistDataState(false);
|
||||||
String cluster = UUID.randomUUID().toString();
|
String cluster = UUID.randomUUID().toString();
|
||||||
List<Member> startupMembers = new ArrayList<>();
|
List<Member> startupMembers = new ArrayList<>();
|
||||||
for (int i = 0; i < clusterMembers; ++i){
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
int id = i + 1;
|
int id = i + 1;
|
||||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (50000 + id));
|
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (50000 + id));
|
||||||
startupMembers.add(new RemoteMember(cluster, uri, id + ""));
|
startupMembers.add(new RemoteMember(cluster, uri, id + ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Member member : startupMembers){
|
for (Member member : startupMembers) {
|
||||||
GossipManager gossipService = GossipManagerBuilder.newBuilder().cluster(cluster).uri(member.getUri())
|
GossipManager gossipService =
|
||||||
.id(member.getId()).gossipMembers(startupMembers).gossipSettings(settings).build();
|
GossipManagerBuilder.newBuilder()
|
||||||
|
.cluster(cluster)
|
||||||
|
.uri(member.getUri())
|
||||||
|
.id(member.getId())
|
||||||
|
.gossipMembers(startupMembers)
|
||||||
|
.gossipSettings(settings)
|
||||||
|
.build();
|
||||||
clients.add(gossipService);
|
clients.add(gossipService);
|
||||||
gossipService.init();
|
gossipService.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void shutdownMembers(){
|
public static void shutdownMembers() {
|
||||||
for (final GossipManager client : clients){
|
for (final GossipManager client : clients) {
|
||||||
client.shutdown();
|
client.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleDataTest(){
|
public void simpleDataTest() {
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
int total = 0;
|
() -> {
|
||||||
for (GossipManager client : clients){
|
int total = 0;
|
||||||
total += client.getLiveMembers().size();
|
for (GossipManager client : clients) {
|
||||||
}
|
total += client.getLiveMembers().size();
|
||||||
return total;
|
}
|
||||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(2);
|
return total;
|
||||||
|
})
|
||||||
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo(2);
|
||||||
|
|
||||||
clients.get(0).gossipPerNodeData(generatePerNodeMsg("a", "b"));
|
clients.get(0).gossipPerNodeData(generatePerNodeMsg("a", "b"));
|
||||||
clients.get(0).gossipSharedData(generateSharedMsg("a", "c"));
|
clients.get(0).gossipSharedData(generateSharedMsg("a", "c"));
|
||||||
|
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
PerNodeDataMessage x = clients.get(1).findPerNodeGossipData(1 + "", "a");
|
() -> {
|
||||||
if (x == null)
|
PerNodeDataMessage x = clients.get(1).findPerNodeGossipData(1 + "", "a");
|
||||||
return "";
|
if (x == null) return "";
|
||||||
else
|
else return x.getPayload();
|
||||||
return x.getPayload();
|
})
|
||||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo("b");
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo("b");
|
||||||
|
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
SharedDataMessage x = clients.get(1).findSharedGossipData("a");
|
() -> {
|
||||||
if (x == null)
|
SharedDataMessage x = clients.get(1).findSharedGossipData("a");
|
||||||
return "";
|
if (x == null) return "";
|
||||||
else
|
else return x.getPayload();
|
||||||
return x.getPayload();
|
})
|
||||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo("c");
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo("c");
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> setFromList(String... elements){
|
Set<String> setFromList(String... elements) {
|
||||||
return new HashSet<>(Arrays.asList(elements));
|
return new HashSet<>(Arrays.asList(elements));
|
||||||
}
|
}
|
||||||
|
|
||||||
void crdtSetTest(String key, Function<Set<String>, CrdtAddRemoveSet<String, Set<String>, ?>> construct){
|
void crdtSetTest(
|
||||||
//populate
|
String key, Function<Set<String>, CrdtAddRemoveSet<String, Set<String>, ?>> construct) {
|
||||||
|
// populate
|
||||||
clients.get(0).merge(generateSharedMsg(key, construct.apply(setFromList("1", "2"))));
|
clients.get(0).merge(generateSharedMsg(key, construct.apply(setFromList("1", "2"))));
|
||||||
clients.get(1).merge(generateSharedMsg(key, construct.apply(setFromList("3", "4"))));
|
clients.get(1).merge(generateSharedMsg(key, construct.apply(setFromList("3", "4"))));
|
||||||
|
|
||||||
assertMergedCrdt(key, construct.apply(setFromList("1", "2", "3", "4")).value());
|
assertMergedCrdt(key, construct.apply(setFromList("1", "2", "3", "4")).value());
|
||||||
|
|
||||||
//drop element
|
// drop element
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
CrdtAddRemoveSet<String, ?, ?> set = (CrdtAddRemoveSet<String, ?, ?>) clients.get(0).findCrdt(key);
|
CrdtAddRemoveSet<String, ?, ?> set =
|
||||||
|
(CrdtAddRemoveSet<String, ?, ?>) clients.get(0).findCrdt(key);
|
||||||
clients.get(0).merge(generateSharedMsg(key, set.remove("3")));
|
clients.get(0).merge(generateSharedMsg(key, set.remove("3")));
|
||||||
|
|
||||||
//assert deletion
|
// assert deletion
|
||||||
assertMergedCrdt(key, construct.apply(setFromList("1", "2", "4")).value());
|
assertMergedCrdt(key, construct.apply(setFromList("1", "2", "4")).value());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void OrSetTest(){
|
public void OrSetTest() {
|
||||||
crdtSetTest("cror", OrSet::new);
|
crdtSetTest("cror", OrSet::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void LWWSetTest(){
|
public void LWWSetTest() {
|
||||||
crdtSetTest("crlww", LwwSet::new);
|
crdtSetTest("crlww", LwwSet::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void MaxChangeSetTest(){
|
public void MaxChangeSetTest() {
|
||||||
crdtSetTest("crmcs", MaxChangeSet::new);
|
crdtSetTest("crmcs", MaxChangeSet::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void TwoPhaseSetTest(){
|
public void TwoPhaseSetTest() {
|
||||||
crdtSetTest("crtps", TwoPhaseSet::new);
|
crdtSetTest("crtps", TwoPhaseSet::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void GrowOnlyCounterTest(){
|
public void GrowOnlyCounterTest() {
|
||||||
Consumer<Long> assertCountUpdated = count -> {
|
Consumer<Long> assertCountUpdated =
|
||||||
for (GossipManager client : clients){
|
count -> {
|
||||||
TUnit.assertThat(() -> client.findCrdt(gCounterKey))
|
for (GossipManager client : clients) {
|
||||||
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
TUnit.assertThat(() -> client.findCrdt(gCounterKey))
|
||||||
.isEqualTo(new GrowOnlyCounter(new GrowOnlyCounter.Builder(client).increment(count)));
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
}
|
.isEqualTo(
|
||||||
};
|
new GrowOnlyCounter(new GrowOnlyCounter.Builder(client).increment(count)));
|
||||||
//generate different increment
|
}
|
||||||
|
};
|
||||||
|
// generate different increment
|
||||||
Object payload = new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(0)).increment(1L));
|
Object payload = new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(0)).increment(1L));
|
||||||
clients.get(0).merge(generateSharedMsg(gCounterKey, payload));
|
clients.get(0).merge(generateSharedMsg(gCounterKey, payload));
|
||||||
payload = new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(1)).increment(2L));
|
payload = new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(1)).increment(2L));
|
||||||
@ -168,30 +184,39 @@ public class DataTest {
|
|||||||
|
|
||||||
assertCountUpdated.accept((long) 3);
|
assertCountUpdated.accept((long) 3);
|
||||||
|
|
||||||
//update one
|
// update one
|
||||||
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(1).findCrdt(gCounterKey);
|
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(1).findCrdt(gCounterKey);
|
||||||
GrowOnlyCounter gc2 = new GrowOnlyCounter(gc,
|
GrowOnlyCounter gc2 =
|
||||||
new GrowOnlyCounter.Builder(clients.get(1)).increment(4L));
|
new GrowOnlyCounter(gc, new GrowOnlyCounter.Builder(clients.get(1)).increment(4L));
|
||||||
clients.get(1).merge(generateSharedMsg(gCounterKey, gc2));
|
clients.get(1).merge(generateSharedMsg(gCounterKey, gc2));
|
||||||
|
|
||||||
assertCountUpdated.accept((long) 7);
|
assertCountUpdated.accept((long) 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void PNCounterTest(){
|
public void PNCounterTest() {
|
||||||
Consumer<List<Integer>> counterUpdate = list -> {
|
Consumer<List<Integer>> counterUpdate =
|
||||||
int clientIndex = 0;
|
list -> {
|
||||||
for (int delta : list){
|
int clientIndex = 0;
|
||||||
PNCounter c = (PNCounter) clients.get(clientIndex).findCrdt(pnCounterKey);
|
for (int delta : list) {
|
||||||
c = new PNCounter(c, new PNCounter.Builder(clients.get(clientIndex)).increment(((long) delta)));
|
PNCounter c = (PNCounter) clients.get(clientIndex).findCrdt(pnCounterKey);
|
||||||
clients.get(clientIndex).merge(generateSharedMsg(pnCounterKey, c));
|
c =
|
||||||
clientIndex = (clientIndex + 1) % clients.size();
|
new PNCounter(
|
||||||
}
|
c, new PNCounter.Builder(clients.get(clientIndex)).increment(((long) delta)));
|
||||||
};
|
clients.get(clientIndex).merge(generateSharedMsg(pnCounterKey, c));
|
||||||
|
clientIndex = (clientIndex + 1) % clients.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// given PNCounter
|
// given PNCounter
|
||||||
clients.get(0).merge(generateSharedMsg(pnCounterKey, new PNCounter(new PNCounter.Builder(clients.get(0)))));
|
clients
|
||||||
clients.get(1).merge(generateSharedMsg(pnCounterKey, new PNCounter(new PNCounter.Builder(clients.get(1)))));
|
.get(0)
|
||||||
|
.merge(
|
||||||
|
generateSharedMsg(pnCounterKey, new PNCounter(new PNCounter.Builder(clients.get(0)))));
|
||||||
|
clients
|
||||||
|
.get(1)
|
||||||
|
.merge(
|
||||||
|
generateSharedMsg(pnCounterKey, new PNCounter(new PNCounter.Builder(clients.get(1)))));
|
||||||
|
|
||||||
assertMergedCrdt(pnCounterKey, (long) 0);
|
assertMergedCrdt(pnCounterKey, (long) 0);
|
||||||
|
|
||||||
@ -203,28 +228,29 @@ public class DataTest {
|
|||||||
|
|
||||||
Long[] expectedResults = {5L, 7L, 9L, 3L};
|
Long[] expectedResults = {5L, 7L, 9L, 3L};
|
||||||
|
|
||||||
for (int i = 0; i < updateLists.size(); i++){
|
for (int i = 0; i < updateLists.size(); i++) {
|
||||||
counterUpdate.accept(updateLists.get(i));
|
counterUpdate.accept(updateLists.get(i));
|
||||||
assertMergedCrdt(pnCounterKey, expectedResults[i]);
|
assertMergedCrdt(pnCounterKey, expectedResults[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void GrowOnlySetTest(){
|
public void GrowOnlySetTest() {
|
||||||
clients.get(0).merge(generateSharedMsg("cr", new GrowOnlySet<>(Arrays.asList("1"))));
|
clients.get(0).merge(generateSharedMsg("cr", new GrowOnlySet<>(Arrays.asList("1"))));
|
||||||
clients.get(1).merge(generateSharedMsg("cr", new GrowOnlySet<>(Arrays.asList("2"))));
|
clients.get(1).merge(generateSharedMsg("cr", new GrowOnlySet<>(Arrays.asList("2"))));
|
||||||
|
|
||||||
assertMergedCrdt("cr", new GrowOnlySet<>(Arrays.asList("1", "2")).value());
|
assertMergedCrdt("cr", new GrowOnlySet<>(Arrays.asList("1", "2")).value());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertMergedCrdt(String key, Object expected){
|
private void assertMergedCrdt(String key, Object expected) {
|
||||||
for (GossipManager client : clients){
|
for (GossipManager client : clients) {
|
||||||
TUnit.assertThat(() -> client.findCrdt(key).value())
|
TUnit.assertThat(() -> client.findCrdt(key).value())
|
||||||
.afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(expected);
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo(expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PerNodeDataMessage generatePerNodeMsg(String key, Object payload){
|
private PerNodeDataMessage generatePerNodeMsg(String key, Object payload) {
|
||||||
PerNodeDataMessage g = new PerNodeDataMessage();
|
PerNodeDataMessage g = new PerNodeDataMessage();
|
||||||
g.setExpireAt(Long.MAX_VALUE);
|
g.setExpireAt(Long.MAX_VALUE);
|
||||||
g.setKey(key);
|
g.setKey(key);
|
||||||
@ -233,7 +259,7 @@ public class DataTest {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SharedDataMessage generateSharedMsg(String key, Object payload){
|
private SharedDataMessage generateSharedMsg(String key, Object payload) {
|
||||||
SharedDataMessage d = new SharedDataMessage();
|
SharedDataMessage d = new SharedDataMessage();
|
||||||
d.setKey(key);
|
d.setKey(key);
|
||||||
d.setPayload(payload);
|
d.setPayload(payload);
|
||||||
@ -241,4 +267,4 @@ public class DataTest {
|
|||||||
d.setTimestamp(System.currentTimeMillis());
|
d.setTimestamp(System.currentTimeMillis());
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
|
import io.teknek.tunit.TUnit;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -26,14 +27,11 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.gossip.manager.DatacenterRackAwareActiveGossiper;
|
import org.apache.gossip.manager.DatacenterRackAwareActiveGossiper;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
|
||||||
|
|
||||||
public class IdAndPropertyTest extends AbstractIntegrationBase {
|
public class IdAndPropertyTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -18,6 +18,12 @@
|
|||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
import io.teknek.tunit.TUnit;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
import org.apache.gossip.model.PerNodeDataMessage;
|
import org.apache.gossip.model.PerNodeDataMessage;
|
||||||
@ -26,13 +32,6 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class PerNodeDataEventTest extends AbstractIntegrationBase {
|
public class PerNodeDataEventTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
import io.teknek.tunit.TUnit;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.gossip.manager.DatacenterRackAwareActiveGossiper;
|
import org.apache.gossip.manager.DatacenterRackAwareActiveGossiper;
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
@ -25,12 +30,6 @@ import org.apache.gossip.model.PerNodeDataMessage;
|
|||||||
import org.apache.gossip.replication.*;
|
import org.apache.gossip.replication.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class PerNodeDataReplicationControlTest extends AbstractIntegrationBase {
|
public class PerNodeDataReplicationControlTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -35,7 +35,7 @@ import org.junit.runners.Parameterized;
|
|||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class SharedDataEventTest extends AbstractIntegrationBase {
|
public class SharedDataEventTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
private String receivedKey = "";
|
private String receivedKey = "";
|
||||||
private Object receivingNodeDataNewValue = "";
|
private Object receivingNodeDataNewValue = "";
|
||||||
private Object receivingNodeDataOldValue = "";
|
private Object receivingNodeDataOldValue = "";
|
||||||
@ -51,14 +51,12 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
|
|
||||||
@Parameterized.Parameters(name = "{index} bulkTransfer={1}")
|
@Parameterized.Parameters(name = "{index} bulkTransfer={1}")
|
||||||
public static Collection<Object[]> data() {
|
public static Collection<Object[]> data() {
|
||||||
return Arrays.asList(new Object[][]{
|
return Arrays.asList(new Object[][] {{50000, false}, {55000, true}});
|
||||||
{50000, false}, {55000, true}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sharedDataEventTest()
|
public void sharedDataEventTest()
|
||||||
throws InterruptedException, UnknownHostException, URISyntaxException {
|
throws InterruptedException, UnknownHostException, URISyntaxException {
|
||||||
GossipSettings settings = new GossipSettings();
|
GossipSettings settings = new GossipSettings();
|
||||||
settings.setPersistRingState(false);
|
settings.setPersistRingState(false);
|
||||||
settings.setPersistDataState(false);
|
settings.setPersistDataState(false);
|
||||||
@ -74,41 +72,52 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
final int clusterMembers = 2;
|
final int clusterMembers = 2;
|
||||||
for (int i = 1; i < clusterMembers + 1; ++i) {
|
for (int i = 1; i < clusterMembers + 1; ++i) {
|
||||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
||||||
GossipManager gossipService = GossipManagerBuilder.newBuilder().cluster(cluster).uri(uri)
|
GossipManager gossipService =
|
||||||
.id(i + "").gossipMembers(startupMembers).gossipSettings(settings).build();
|
GossipManagerBuilder.newBuilder()
|
||||||
|
.cluster(cluster)
|
||||||
|
.uri(uri)
|
||||||
|
.id(i + "")
|
||||||
|
.gossipMembers(startupMembers)
|
||||||
|
.gossipSettings(settings)
|
||||||
|
.build();
|
||||||
clients.add(gossipService);
|
clients.add(gossipService);
|
||||||
gossipService.init();
|
gossipService.init();
|
||||||
register(gossipService);
|
register(gossipService);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the members are discovered
|
// check whether the members are discovered
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
int total = 0;
|
() -> {
|
||||||
for (int i = 0; i < clusterMembers; ++i) {
|
int total = 0;
|
||||||
total += clients.get(i).getLiveMembers().size();
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
}
|
total += clients.get(i).getLiveMembers().size();
|
||||||
return total;
|
}
|
||||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo(2);
|
return total;
|
||||||
|
})
|
||||||
|
.afterWaitingAtMost(20, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo(2);
|
||||||
|
|
||||||
// Adding new data to Node 1
|
// Adding new data to Node 1
|
||||||
clients.get(0).gossipSharedData(sharedNodeData("category", "distributed"));
|
clients.get(0).gossipSharedData(sharedNodeData("category", "distributed"));
|
||||||
|
|
||||||
// Node 2 is interested in data changes for the key "organization" and "category"
|
// Node 2 is interested in data changes for the key "organization" and "category"
|
||||||
clients.get(1).registerSharedDataSubscriber((key, oldValue, newValue) -> {
|
clients
|
||||||
if (!key.equals("organization") && !key.equals("category"))
|
.get(1)
|
||||||
return;
|
.registerSharedDataSubscriber(
|
||||||
receivedKey = key;
|
(key, oldValue, newValue) -> {
|
||||||
receivingNodeDataOldValue = oldValue;
|
if (!key.equals("organization") && !key.equals("category")) return;
|
||||||
receivingNodeDataNewValue = newValue;
|
receivedKey = key;
|
||||||
lock.release();
|
receivingNodeDataOldValue = oldValue;
|
||||||
});
|
receivingNodeDataNewValue = newValue;
|
||||||
|
lock.release();
|
||||||
|
});
|
||||||
|
|
||||||
// Node 2 first time gets shared data
|
// Node 2 first time gets shared data
|
||||||
lock.tryAcquire(10, TimeUnit.SECONDS);
|
lock.tryAcquire(10, TimeUnit.SECONDS);
|
||||||
Assert.assertEquals("category", receivedKey);
|
Assert.assertEquals("category", receivedKey);
|
||||||
Assert.assertEquals(null, receivingNodeDataOldValue);
|
Assert.assertEquals(null, receivingNodeDataOldValue);
|
||||||
Assert.assertEquals("distributed", receivingNodeDataNewValue);
|
Assert.assertEquals("distributed", receivingNodeDataNewValue);
|
||||||
|
|
||||||
// Node 1 adds new per node data
|
// Node 1 adds new per node data
|
||||||
clients.get(0).gossipSharedData(sharedNodeData("organization", "apache"));
|
clients.get(0).gossipSharedData(sharedNodeData("organization", "apache"));
|
||||||
// Node 2 adds new shared data
|
// Node 2 adds new shared data
|
||||||
@ -116,21 +125,20 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
Assert.assertEquals("organization", receivedKey);
|
Assert.assertEquals("organization", receivedKey);
|
||||||
Assert.assertEquals(null, receivingNodeDataOldValue);
|
Assert.assertEquals(null, receivingNodeDataOldValue);
|
||||||
Assert.assertEquals("apache", receivingNodeDataNewValue);
|
Assert.assertEquals("apache", receivingNodeDataNewValue);
|
||||||
|
|
||||||
// Node 1 updates its value
|
// Node 1 updates its value
|
||||||
clients.get(0).gossipSharedData(sharedNodeData("organization", "apache-gossip"));
|
clients.get(0).gossipSharedData(sharedNodeData("organization", "apache-gossip"));
|
||||||
|
|
||||||
// Node 2 updates existing value
|
// Node 2 updates existing value
|
||||||
lock.tryAcquire(10, TimeUnit.SECONDS);
|
lock.tryAcquire(10, TimeUnit.SECONDS);
|
||||||
Assert.assertEquals("organization", receivedKey);
|
Assert.assertEquals("organization", receivedKey);
|
||||||
Assert.assertEquals("apache", receivingNodeDataOldValue);
|
Assert.assertEquals("apache", receivingNodeDataOldValue);
|
||||||
Assert.assertEquals("apache-gossip", receivingNodeDataNewValue);
|
Assert.assertEquals("apache-gossip", receivingNodeDataNewValue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void CrdtDataChangeEventTest()
|
public void CrdtDataChangeEventTest()
|
||||||
throws InterruptedException, UnknownHostException, URISyntaxException {
|
throws InterruptedException, UnknownHostException, URISyntaxException {
|
||||||
GossipSettings settings = new GossipSettings();
|
GossipSettings settings = new GossipSettings();
|
||||||
settings.setPersistRingState(false);
|
settings.setPersistRingState(false);
|
||||||
settings.setPersistDataState(false);
|
settings.setPersistDataState(false);
|
||||||
@ -145,29 +153,41 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
final int clusterMembers = 3;
|
final int clusterMembers = 3;
|
||||||
for (int i = 1; i < clusterMembers + 1; ++i) {
|
for (int i = 1; i < clusterMembers + 1; ++i) {
|
||||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
||||||
GossipManager gossipService = GossipManagerBuilder.newBuilder().cluster(cluster).uri(uri)
|
GossipManager gossipService =
|
||||||
.id(i + "").gossipMembers(startupMembers).gossipSettings(settings).build();
|
GossipManagerBuilder.newBuilder()
|
||||||
|
.cluster(cluster)
|
||||||
|
.uri(uri)
|
||||||
|
.id(i + "")
|
||||||
|
.gossipMembers(startupMembers)
|
||||||
|
.gossipSettings(settings)
|
||||||
|
.build();
|
||||||
clients.add(gossipService);
|
clients.add(gossipService);
|
||||||
gossipService.init();
|
gossipService.init();
|
||||||
register(gossipService);
|
register(gossipService);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the members are discovered
|
// check whether the members are discovered
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
int total = 0;
|
() -> {
|
||||||
for (int i = 0; i < clusterMembers; ++i) {
|
int total = 0;
|
||||||
total += clients.get(i).getLiveMembers().size();
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
}
|
total += clients.get(i).getLiveMembers().size();
|
||||||
return total;
|
}
|
||||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo(2);
|
return total;
|
||||||
|
})
|
||||||
clients.get(1).registerSharedDataSubscriber((key, oldValue, newValue) -> {
|
.afterWaitingAtMost(20, TimeUnit.SECONDS)
|
||||||
receivedKey = key;
|
.isEqualTo(2);
|
||||||
receivingNodeDataOldValue = oldValue;
|
|
||||||
receivingNodeDataNewValue = newValue;
|
clients
|
||||||
lock.release();
|
.get(1)
|
||||||
});
|
.registerSharedDataSubscriber(
|
||||||
|
(key, oldValue, newValue) -> {
|
||||||
|
receivedKey = key;
|
||||||
|
receivingNodeDataOldValue = oldValue;
|
||||||
|
receivingNodeDataNewValue = newValue;
|
||||||
|
lock.release();
|
||||||
|
});
|
||||||
|
|
||||||
// Add initial gCounter to Node 1
|
// Add initial gCounter to Node 1
|
||||||
SharedDataMessage d = new SharedDataMessage();
|
SharedDataMessage d = new SharedDataMessage();
|
||||||
d.setKey(gCounterKey);
|
d.setKey(gCounterKey);
|
||||||
@ -175,37 +195,39 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
d.setExpireAt(Long.MAX_VALUE);
|
d.setExpireAt(Long.MAX_VALUE);
|
||||||
d.setTimestamp(System.currentTimeMillis());
|
d.setTimestamp(System.currentTimeMillis());
|
||||||
clients.get(0).merge(d);
|
clients.get(0).merge(d);
|
||||||
|
|
||||||
// Check if initial Crdt received
|
// Check if initial Crdt received
|
||||||
lock.tryAcquire(10, TimeUnit.SECONDS);
|
lock.tryAcquire(10, TimeUnit.SECONDS);
|
||||||
Assert.assertEquals("gCounter", receivedKey);
|
Assert.assertEquals("gCounter", receivedKey);
|
||||||
Assert.assertEquals(null, receivingNodeDataOldValue);
|
Assert.assertEquals(null, receivingNodeDataOldValue);
|
||||||
Assert.assertTrue(receivingNodeDataNewValue instanceof GrowOnlyCounter);
|
Assert.assertTrue(receivingNodeDataNewValue instanceof GrowOnlyCounter);
|
||||||
Assert.assertEquals(1, ((GrowOnlyCounter) receivingNodeDataNewValue).value().longValue());
|
Assert.assertEquals(1, ((GrowOnlyCounter) receivingNodeDataNewValue).value().longValue());
|
||||||
|
|
||||||
// check whether Node 3 received the gCounter
|
// check whether Node 3 received the gCounter
|
||||||
TUnit.assertThat(() -> {
|
TUnit.assertThat(
|
||||||
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(2).findCrdt(gCounterKey);
|
() -> {
|
||||||
if (gc == null) {
|
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(2).findCrdt(gCounterKey);
|
||||||
return "";
|
if (gc == null) {
|
||||||
} else {
|
return "";
|
||||||
return gc;
|
} else {
|
||||||
}
|
return gc;
|
||||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(
|
}
|
||||||
new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(0)).increment(1L)));
|
})
|
||||||
|
.afterWaitingAtMost(10, TimeUnit.SECONDS)
|
||||||
|
.isEqualTo(new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(0)).increment(1L)));
|
||||||
|
|
||||||
// Node 3 Updates the gCounter by 4
|
// Node 3 Updates the gCounter by 4
|
||||||
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(2).findCrdt(gCounterKey);
|
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(2).findCrdt(gCounterKey);
|
||||||
GrowOnlyCounter gcNew = new GrowOnlyCounter(gc,
|
GrowOnlyCounter gcNew =
|
||||||
new GrowOnlyCounter.Builder(clients.get(2)).increment(4L));
|
new GrowOnlyCounter(gc, new GrowOnlyCounter.Builder(clients.get(2)).increment(4L));
|
||||||
|
|
||||||
d = new SharedDataMessage();
|
d = new SharedDataMessage();
|
||||||
d.setKey(gCounterKey);
|
d.setKey(gCounterKey);
|
||||||
d.setPayload(gcNew);
|
d.setPayload(gcNew);
|
||||||
d.setExpireAt(Long.MAX_VALUE);
|
d.setExpireAt(Long.MAX_VALUE);
|
||||||
d.setTimestamp(System.currentTimeMillis());
|
d.setTimestamp(System.currentTimeMillis());
|
||||||
clients.get(2).merge(d);
|
clients.get(2).merge(d);
|
||||||
|
|
||||||
// Check if Node 3's Crdt update is received in Node 2 event handler
|
// Check if Node 3's Crdt update is received in Node 2 event handler
|
||||||
lock.tryAcquire(10, TimeUnit.SECONDS);
|
lock.tryAcquire(10, TimeUnit.SECONDS);
|
||||||
Assert.assertEquals("gCounter", receivedKey);
|
Assert.assertEquals("gCounter", receivedKey);
|
||||||
@ -213,9 +235,8 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
Assert.assertEquals(1, ((GrowOnlyCounter) receivingNodeDataOldValue).value().longValue());
|
Assert.assertEquals(1, ((GrowOnlyCounter) receivingNodeDataOldValue).value().longValue());
|
||||||
Assert.assertTrue(receivingNodeDataNewValue instanceof GrowOnlyCounter);
|
Assert.assertTrue(receivingNodeDataNewValue instanceof GrowOnlyCounter);
|
||||||
Assert.assertEquals(5, ((GrowOnlyCounter) receivingNodeDataNewValue).value().longValue());
|
Assert.assertEquals(5, ((GrowOnlyCounter) receivingNodeDataNewValue).value().longValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SharedDataMessage sharedNodeData(String key, String value) {
|
private SharedDataMessage sharedNodeData(String key, String value) {
|
||||||
SharedDataMessage g = new SharedDataMessage();
|
SharedDataMessage g = new SharedDataMessage();
|
||||||
g.setExpireAt(Long.MAX_VALUE);
|
g.setExpireAt(Long.MAX_VALUE);
|
||||||
@ -224,5 +245,4 @@ public class SharedDataEventTest extends AbstractIntegrationBase {
|
|||||||
g.setTimestamp(System.currentTimeMillis());
|
g.setTimestamp(System.currentTimeMillis());
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
import org.apache.gossip.lock.exceptions.VoteFailedException;
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
|
||||||
import org.apache.gossip.model.SharedDataMessage;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -31,6 +24,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import org.apache.gossip.lock.exceptions.VoteFailedException;
|
||||||
|
import org.apache.gossip.manager.GossipManager;
|
||||||
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
|
import org.apache.gossip.model.SharedDataMessage;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class SharedDataLockTest extends AbstractIntegrationBase {
|
public class SharedDataLockTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
|
@ -44,8 +44,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class SharedDataReplicationControlTest extends AbstractIntegrationBase {
|
public class SharedDataReplicationControlTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sharedDataReplicationTest()
|
public void sharedDataReplicationTest() throws URISyntaxException {
|
||||||
throws URISyntaxException {
|
|
||||||
generateStandardNodes(3);
|
generateStandardNodes(3);
|
||||||
|
|
||||||
// check whether the members are discovered
|
// check whether the members are discovered
|
||||||
|
@ -38,8 +38,7 @@ public class ShutdownDeadtimeTest {
|
|||||||
// harm), and the
|
// harm), and the
|
||||||
// sleep that happens after startup.
|
// sleep that happens after startup.
|
||||||
@Test
|
@Test
|
||||||
public void DeadNodesDoNotComeAliveAgain()
|
public void DeadNodesDoNotComeAliveAgain() throws InterruptedException, URISyntaxException {
|
||||||
throws InterruptedException, URISyntaxException {
|
|
||||||
GossipSettings settings = new GossipSettings(100, 10000, 1000, 1, 10.0, "normal", false);
|
GossipSettings settings = new GossipSettings(100, 10000, 1000, 1, 10.0, "normal", false);
|
||||||
settings.setPersistRingState(false);
|
settings.setPersistRingState(false);
|
||||||
settings.setPersistDataState(false);
|
settings.setPersistDataState(false);
|
||||||
@ -68,13 +67,13 @@ public class ShutdownDeadtimeTest {
|
|||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
}
|
}
|
||||||
TUnit.assertThat(
|
TUnit.assertThat(
|
||||||
() -> {
|
() -> {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < clusterMembers; ++i) {
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
total += clients.get(i).getLiveMembers().size();
|
total += clients.get(i).getLiveMembers().size();
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
})
|
})
|
||||||
.afterWaitingAtMost(40, TimeUnit.SECONDS)
|
.afterWaitingAtMost(40, TimeUnit.SECONDS)
|
||||||
.isEqualTo(20);
|
.isEqualTo(20);
|
||||||
|
|
||||||
@ -86,25 +85,25 @@ public class ShutdownDeadtimeTest {
|
|||||||
final String shutdownId = clients.get(randomClientId).getMyself().getId();
|
final String shutdownId = clients.get(randomClientId).getMyself().getId();
|
||||||
clients.get(randomClientId).shutdown();
|
clients.get(randomClientId).shutdown();
|
||||||
TUnit.assertThat(
|
TUnit.assertThat(
|
||||||
() -> {
|
() -> {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < clusterMembers; ++i) {
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
total += clients.get(i).getLiveMembers().size();
|
total += clients.get(i).getLiveMembers().size();
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
})
|
})
|
||||||
.afterWaitingAtMost(40, TimeUnit.SECONDS)
|
.afterWaitingAtMost(40, TimeUnit.SECONDS)
|
||||||
.isEqualTo(16);
|
.isEqualTo(16);
|
||||||
clients.remove(randomClientId);
|
clients.remove(randomClientId);
|
||||||
|
|
||||||
TUnit.assertThat(
|
TUnit.assertThat(
|
||||||
() -> {
|
() -> {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < clusterMembers - 1; ++i) {
|
for (int i = 0; i < clusterMembers - 1; ++i) {
|
||||||
total += clients.get(i).getDeadMembers().size();
|
total += clients.get(i).getDeadMembers().size();
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
})
|
})
|
||||||
.afterWaitingAtMost(50, TimeUnit.SECONDS)
|
.afterWaitingAtMost(50, TimeUnit.SECONDS)
|
||||||
.isEqualTo(4);
|
.isEqualTo(4);
|
||||||
|
|
||||||
@ -123,13 +122,13 @@ public class ShutdownDeadtimeTest {
|
|||||||
|
|
||||||
// verify that the client is alive again for every node
|
// verify that the client is alive again for every node
|
||||||
TUnit.assertThat(
|
TUnit.assertThat(
|
||||||
() -> {
|
() -> {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < clusterMembers; ++i) {
|
for (int i = 0; i < clusterMembers; ++i) {
|
||||||
total += clients.get(i).getLiveMembers().size();
|
total += clients.get(i).getLiveMembers().size();
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
})
|
})
|
||||||
.afterWaitingAtMost(60, TimeUnit.SECONDS)
|
.afterWaitingAtMost(60, TimeUnit.SECONDS)
|
||||||
.isEqualTo(20);
|
.isEqualTo(20);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
|
import io.teknek.tunit.TUnit;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -28,7 +29,6 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
import org.apache.gossip.manager.GossipManager;
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
import org.apache.gossip.manager.PassiveGossipConstants;
|
import org.apache.gossip.manager.PassiveGossipConstants;
|
||||||
@ -36,8 +36,6 @@ import org.apache.gossip.secure.KeyTool;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
|
||||||
|
|
||||||
public class SignedMessageTest extends AbstractIntegrationBase {
|
public class SignedMessageTest extends AbstractIntegrationBase {
|
||||||
|
|
||||||
private GossipSettings gossiperThatSigns() {
|
private GossipSettings gossiperThatSigns() {
|
||||||
|
@ -17,18 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
|
||||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.gossip.manager.GossipManager;
|
||||||
|
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/** Tests support of using {@code StartupSettings} and thereby reading setup config from file. */
|
/** Tests support of using {@code StartupSettings} and thereby reading setup config from file. */
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package org.apache.gossip;
|
package org.apache.gossip;
|
||||||
|
|
||||||
import io.teknek.tunit.TUnit;
|
import io.teknek.tunit.TUnit;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
@ -21,13 +21,6 @@ import com.codahale.metrics.Meter;
|
|||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.gossip.GossipSettings;
|
|
||||||
import org.apache.gossip.crdt.CrdtModule;
|
|
||||||
import org.apache.gossip.manager.PassiveGossipConstants;
|
|
||||||
import org.apache.gossip.model.Base;
|
|
||||||
import org.apache.gossip.model.SignedPayload;
|
|
||||||
import org.apache.gossip.protocol.ProtocolManager;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -40,6 +33,12 @@ import java.security.Signature;
|
|||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import org.apache.gossip.GossipSettings;
|
||||||
|
import org.apache.gossip.crdt.CrdtModule;
|
||||||
|
import org.apache.gossip.manager.PassiveGossipConstants;
|
||||||
|
import org.apache.gossip.model.Base;
|
||||||
|
import org.apache.gossip.model.SignedPayload;
|
||||||
|
import org.apache.gossip.protocol.ProtocolManager;
|
||||||
|
|
||||||
// this class is constructed by reflection in GossipManager.
|
// this class is constructed by reflection in GossipManager.
|
||||||
public class JacksonProtocolManager implements ProtocolManager {
|
public class JacksonProtocolManager implements ProtocolManager {
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
package org.apache.gossip.protocol.json;
|
package org.apache.gossip.protocol.json;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -38,8 +40,6 @@ import org.apache.gossip.protocol.ProtocolManager;
|
|||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class JacksonTest {
|
public class JacksonTest {
|
||||||
|
|
||||||
private static GossipSettings simpleSettings(GossipSettings settings) {
|
private static GossipSettings simpleSettings(GossipSettings settings) {
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.protocol.json;
|
package org.apache.gossip.protocol.json;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.apache.gossip.model.Base;
|
|
||||||
import org.apache.gossip.udp.Trackable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.gossip.model.Base;
|
||||||
|
import org.apache.gossip.udp.Trackable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Here is a test class for serialization. I've tried to include a lot of things in it including nested classes.
|
* Here is a test class for serialization. I've tried to include a lot of things in it including nested classes.
|
||||||
|
@ -17,12 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.gossip.transport.udp;
|
package org.apache.gossip.transport.udp;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.gossip.manager.GossipCore;
|
|
||||||
import org.apache.gossip.manager.GossipManager;
|
|
||||||
import org.apache.gossip.model.Base;
|
|
||||||
import org.apache.gossip.transport.AbstractTransportManager;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
@ -32,6 +26,11 @@ import java.net.SocketAddress;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.gossip.manager.GossipCore;
|
||||||
|
import org.apache.gossip.manager.GossipManager;
|
||||||
|
import org.apache.gossip.model.Base;
|
||||||
|
import org.apache.gossip.transport.AbstractTransportManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is constructed by reflection in GossipManager. It manages transport (byte read/write)
|
* This class is constructed by reflection in GossipManager. It manages transport (byte read/write)
|
||||||
|
Reference in New Issue
Block a user