GOSSIP-85 Factor out PassiveGossipThread
This commit is contained in:
@ -44,7 +44,7 @@ public class GossipSettings {
|
||||
private String distribution = "normal";
|
||||
|
||||
private String activeGossipClass = "org.apache.gossip.manager.SimpleActiveGossipper";
|
||||
|
||||
|
||||
private String transportManagerClass = "org.apache.gossip.transport.udp.UdpTransportManager";
|
||||
private String protocolManagerClass = "org.apache.gossip.protocol.json.JacksonProtocolManager";
|
||||
|
||||
@ -241,4 +241,5 @@ public class GossipSettings {
|
||||
public void setProtocolManagerClass(String protocolManagerClass) {
|
||||
this.protocolManagerClass = protocolManagerClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ public abstract class GossipManager {
|
||||
public static final Logger LOGGER = Logger.getLogger(GossipManager.class);
|
||||
|
||||
// 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 = new ObjectMapper() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
{
|
||||
enableDefaultTyping();
|
||||
configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, false);
|
||||
}};
|
||||
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.gossip.model.Base;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* This class handles the passive cycle,
|
||||
* where this client has received an incoming message.
|
||||
*/
|
||||
public class PassiveGossipThread implements Runnable {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger(PassiveGossipThread.class);
|
||||
|
||||
|
||||
private final AtomicBoolean keepRunning;
|
||||
private final GossipCore gossipCore;
|
||||
private final GossipManager gossipManager;
|
||||
|
||||
public PassiveGossipThread(GossipManager gossipManager, GossipCore gossipCore) {
|
||||
this.gossipManager = gossipManager;
|
||||
this.gossipCore = gossipCore;
|
||||
if (gossipManager.getMyself().getClusterName() == null){
|
||||
throw new IllegalArgumentException("Cluster was null");
|
||||
}
|
||||
|
||||
keepRunning = new AtomicBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (keepRunning.get()) {
|
||||
try {
|
||||
byte[] buf = gossipManager.getTransportManager().read();
|
||||
try {
|
||||
Base message = gossipManager.getProtocolManager().read(buf);
|
||||
gossipCore.receive(message);
|
||||
gossipManager.getMemberStateRefresher().run();
|
||||
} catch (RuntimeException ex) {//TODO trap json exception
|
||||
LOGGER.error("Unable to process message", ex);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// InterruptedException are completely normal here because of the blocking lifecycle.
|
||||
if (!(e.getCause() instanceof InterruptedException)) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
keepRunning.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void requestStop() {
|
||||
keepRunning.set(false);
|
||||
}
|
||||
}
|
@ -22,14 +22,11 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.gossip.LocalMember;
|
||||
import org.apache.gossip.crdt.CrdtModule;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class RingStatePersister implements Runnable {
|
||||
|
@ -21,7 +21,6 @@ import com.codahale.metrics.MetricRegistry;
|
||||
import org.apache.gossip.manager.AbstractActiveGossiper;
|
||||
import org.apache.gossip.manager.GossipCore;
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.PassiveGossipThread;
|
||||
import org.apache.gossip.utils.ReflectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -36,14 +35,14 @@ public abstract class AbstractTransportManager implements TransportManager {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger(AbstractTransportManager.class);
|
||||
|
||||
private final PassiveGossipThread passiveGossipThread;
|
||||
private final ExecutorService gossipThreadExecutor;
|
||||
|
||||
private final AbstractActiveGossiper activeGossipThread;
|
||||
protected final GossipManager gossipManager;
|
||||
protected final GossipCore gossipCore;
|
||||
|
||||
public AbstractTransportManager(GossipManager gossipManager, GossipCore gossipCore) {
|
||||
|
||||
passiveGossipThread = new PassiveGossipThread(gossipManager, gossipCore);
|
||||
this.gossipManager = gossipManager;
|
||||
this.gossipCore = gossipCore;
|
||||
gossipThreadExecutor = Executors.newCachedThreadPool();
|
||||
activeGossipThread = ReflectionUtils.constructWithReflection(
|
||||
gossipManager.getSettings().getActiveGossipClass(),
|
||||
@ -58,7 +57,6 @@ public abstract class AbstractTransportManager implements TransportManager {
|
||||
// shut down threads etc.
|
||||
@Override
|
||||
public void shutdown() {
|
||||
passiveGossipThread.requestStop();
|
||||
gossipThreadExecutor.shutdown();
|
||||
if (activeGossipThread != null) {
|
||||
activeGossipThread.shutdown();
|
||||
@ -77,11 +75,9 @@ public abstract class AbstractTransportManager implements TransportManager {
|
||||
|
||||
@Override
|
||||
public void startActiveGossiper() {
|
||||
activeGossipThread.init();
|
||||
activeGossipThread.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startEndpoint() {
|
||||
gossipThreadExecutor.execute(passiveGossipThread);
|
||||
}
|
||||
public abstract void startEndpoint();
|
||||
}
|
||||
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.gossip.crdt.GrowOnlyCounter;
|
||||
import org.apache.gossip.crdt.GrowOnlySet;
|
||||
import org.apache.gossip.crdt.OrSet;
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.apache.gossip.model.PerNodeDataMessage;
|
||||
import org.apache.gossip.model.SharedDataMessage;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.teknek.tunit.TUnit;
|
||||
|
||||
public class DataTest extends AbstractIntegrationBase {
|
||||
|
||||
private String orSetKey = "cror";
|
||||
private String gCounterKey = "crdtgc";
|
||||
|
||||
@Test
|
||||
public void dataTest() throws InterruptedException, UnknownHostException, URISyntaxException{
|
||||
GossipSettings settings = new GossipSettings();
|
||||
settings.setPersistRingState(false);
|
||||
settings.setPersistDataState(false);
|
||||
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
String cluster = UUID.randomUUID().toString();
|
||||
int seedNodes = 1;
|
||||
List<Member> startupMembers = new ArrayList<>();
|
||||
for (int i = 1; i < seedNodes+1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (50000 + i));
|
||||
startupMembers.add(new RemoteMember(cluster, uri, i + ""));
|
||||
}
|
||||
final List<GossipManager> clients = new ArrayList<>();
|
||||
final int clusterMembers = 2;
|
||||
for (int i = 1; i < clusterMembers + 1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (50000 + i));
|
||||
GossipManager gossipService = GossipManagerBuilder.newBuilder().cluster(cluster).uri(uri)
|
||||
.id(i + "").gossipMembers(startupMembers).gossipSettings(settings).build();
|
||||
clients.add(gossipService);
|
||||
gossipService.init();
|
||||
register(gossipService);
|
||||
}
|
||||
TUnit.assertThat(() -> {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo(2);
|
||||
clients.get(0).gossipPerNodeData(msg());
|
||||
clients.get(0).gossipSharedData(sharedMsg());
|
||||
|
||||
TUnit.assertThat(()-> {
|
||||
PerNodeDataMessage x = clients.get(1).findPerNodeGossipData(1 + "", "a");
|
||||
if (x == null)
|
||||
return "";
|
||||
else
|
||||
return x.getPayload();
|
||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo("b");
|
||||
|
||||
TUnit.assertThat(() -> {
|
||||
SharedDataMessage x = clients.get(1).findSharedGossipData("a");
|
||||
if (x == null)
|
||||
return "";
|
||||
else
|
||||
return x.getPayload();
|
||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo("c");
|
||||
|
||||
|
||||
givenDifferentDatumsInSet(clients);
|
||||
assertThatListIsMerged(clients);
|
||||
|
||||
givenOrs(clients);
|
||||
assertThatOrSetIsMerged(clients);
|
||||
dropIt(clients);
|
||||
assertThatOrSetDelIsMerged(clients);
|
||||
|
||||
|
||||
// test g counter
|
||||
givenDifferentIncrement(clients);
|
||||
assertThatCountIsUpdated(clients, 3);
|
||||
givenIncreaseOther(clients);
|
||||
assertThatCountIsUpdated(clients, 7);
|
||||
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
clients.get(i).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void givenDifferentIncrement(final List<GossipManager> clients) {
|
||||
{
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(gCounterKey);
|
||||
d.setPayload(new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(0)).increment(1L)));
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(0).merge(d);
|
||||
}
|
||||
{
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(gCounterKey);
|
||||
d.setPayload(new GrowOnlyCounter(new GrowOnlyCounter.Builder(clients.get(1)).increment(2L)));
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(1).merge(d);
|
||||
}
|
||||
}
|
||||
|
||||
private void givenIncreaseOther(final List<GossipManager> clients) {
|
||||
GrowOnlyCounter gc = (GrowOnlyCounter) clients.get(1).findCrdt(gCounterKey);
|
||||
GrowOnlyCounter gc2 = new GrowOnlyCounter(gc,
|
||||
new GrowOnlyCounter.Builder(clients.get(1)).increment(4L));
|
||||
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(gCounterKey);
|
||||
d.setPayload(gc2);
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(1).merge(d);
|
||||
}
|
||||
|
||||
private void givenOrs(List<GossipManager> clients) {
|
||||
{
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(orSetKey);
|
||||
d.setPayload(new OrSet<String>("1", "2"));
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(0).merge(d);
|
||||
}
|
||||
{
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(orSetKey);
|
||||
d.setPayload(new OrSet<String>("3", "4"));
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(1).merge(d);
|
||||
}
|
||||
}
|
||||
|
||||
private void dropIt(List<GossipManager> clients) {
|
||||
@SuppressWarnings("unchecked")
|
||||
OrSet<String> o = (OrSet<String>) clients.get(0).findCrdt(orSetKey);
|
||||
OrSet<String> o2 = new OrSet<String>(o, new OrSet.Builder<String>().remove("3"));
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey(orSetKey);
|
||||
d.setPayload(o2);
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
clients.get(0).merge(d);
|
||||
}
|
||||
|
||||
private void assertThatOrSetIsMerged(final List<GossipManager> clients){
|
||||
TUnit.assertThat(() -> {
|
||||
return clients.get(0).findCrdt(orSetKey).value();
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(new OrSet<String>("1", "2", "3", "4").value());
|
||||
TUnit.assertThat(() -> {
|
||||
return clients.get(1).findCrdt(orSetKey).value();
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(new OrSet<String>("1", "2", "3", "4").value());
|
||||
}
|
||||
|
||||
private void assertThatOrSetDelIsMerged(final List<GossipManager> clients){
|
||||
TUnit.assertThat(() -> {
|
||||
return clients.get(0).findCrdt(orSetKey);
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).equals(new OrSet<String>("1", "2", "4"));
|
||||
}
|
||||
|
||||
private void givenDifferentDatumsInSet(final List<GossipManager> clients){
|
||||
clients.get(0).merge(CrdtMessage("1"));
|
||||
clients.get(1).merge(CrdtMessage("2"));
|
||||
}
|
||||
|
||||
|
||||
private void assertThatCountIsUpdated(final List<GossipManager> clients, long finalCount) {
|
||||
TUnit.assertThat(() -> {
|
||||
return clients.get(0).findCrdt(gCounterKey);
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(new GrowOnlyCounter(
|
||||
new GrowOnlyCounter.Builder(clients.get(0)).increment(finalCount)));
|
||||
}
|
||||
|
||||
private void assertThatListIsMerged(final List<GossipManager> clients){
|
||||
TUnit.assertThat(() -> {
|
||||
return clients.get(0).findCrdt("cr");
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo(new GrowOnlySet<String>(Arrays.asList("1","2")));
|
||||
}
|
||||
|
||||
private SharedDataMessage CrdtMessage(String item){
|
||||
SharedDataMessage d = new SharedDataMessage();
|
||||
d.setKey("cr");
|
||||
d.setPayload(new GrowOnlySet<String>( Arrays.asList(item)));
|
||||
d.setExpireAt(Long.MAX_VALUE);
|
||||
d.setTimestamp(System.currentTimeMillis());
|
||||
return d;
|
||||
}
|
||||
|
||||
private PerNodeDataMessage msg(){
|
||||
PerNodeDataMessage g = new PerNodeDataMessage();
|
||||
g.setExpireAt(Long.MAX_VALUE);
|
||||
g.setKey("a");
|
||||
g.setPayload("b");
|
||||
g.setTimestamp(System.currentTimeMillis());
|
||||
return g;
|
||||
}
|
||||
|
||||
private SharedDataMessage sharedMsg(){
|
||||
SharedDataMessage g = new SharedDataMessage();
|
||||
g.setExpireAt(Long.MAX_VALUE);
|
||||
g.setKey("a");
|
||||
g.setPayload("c");
|
||||
g.setTimestamp(System.currentTimeMillis());
|
||||
return g;
|
||||
}
|
||||
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.gossip.manager.DatacenterRackAwareActiveGossiper;
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.platform.runner.JUnitPlatform;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import io.teknek.tunit.TUnit;
|
||||
|
||||
@RunWith(JUnitPlatform.class)
|
||||
public class IdAndPropertyTest extends AbstractIntegrationBase {
|
||||
|
||||
@Test
|
||||
public void testDatacenterRackGossiper() throws URISyntaxException, UnknownHostException, InterruptedException {
|
||||
GossipSettings settings = new GossipSettings();
|
||||
settings.setActiveGossipClass(DatacenterRackAwareActiveGossiper.class.getName());
|
||||
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
List<Member> startupMembers = new ArrayList<>();
|
||||
Map<String, String> x = new HashMap<>();
|
||||
x.put("a", "b");
|
||||
x.put("datacenter", "dc1");
|
||||
x.put("rack", "rack1");
|
||||
GossipManager gossipService1 = GossipManagerBuilder.newBuilder()
|
||||
.cluster("a")
|
||||
.uri(new URI("udp://" + "127.0.0.1" + ":" + (29000 + 0)))
|
||||
.id("0")
|
||||
.properties(x)
|
||||
.gossipMembers(startupMembers)
|
||||
.gossipSettings(settings).build();
|
||||
gossipService1.init();
|
||||
register(gossipService1);
|
||||
|
||||
Map<String, String> y = new HashMap<>();
|
||||
y.put("a", "c");
|
||||
y.put("datacenter", "dc2");
|
||||
y.put("rack", "rack2");
|
||||
GossipManager gossipService2 = GossipManagerBuilder.newBuilder().cluster("a")
|
||||
.uri( new URI("udp://" + "127.0.0.1" + ":" + (29000 + 10)))
|
||||
.id("1")
|
||||
.properties(y)
|
||||
.gossipMembers(Arrays.asList(new RemoteMember("a",
|
||||
new URI("udp://" + "127.0.0.1" + ":" + (29000 + 0)), "0")))
|
||||
.gossipSettings(settings).build();
|
||||
gossipService2.init();
|
||||
register(gossipService2);
|
||||
|
||||
TUnit.assertThat(() -> {
|
||||
String value = "";
|
||||
try {
|
||||
value = gossipService1.getLiveMembers().get(0).getProperties().get("a");
|
||||
} catch (RuntimeException e){ }
|
||||
return value;
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo("c");
|
||||
|
||||
TUnit.assertThat(() -> {
|
||||
String value = "";
|
||||
try {
|
||||
value = gossipService2.getLiveMembers().get(0).getProperties().get("a");
|
||||
} catch (RuntimeException e){ }
|
||||
return value;
|
||||
}).afterWaitingAtMost(10, TimeUnit.SECONDS).isEqualTo("b");
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import io.teknek.tunit.TUnit;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.junit.platform.runner.JUnitPlatform;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(JUnitPlatform.class)
|
||||
public class ShutdownDeadtimeTest {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ShutdownDeadtimeTest.class);
|
||||
|
||||
// Note: this test is floppy depending on the values in GossipSettings (smaller values seem to do harm), and the
|
||||
// sleep that happens after startup.
|
||||
@Test
|
||||
public void DeadNodesDoNotComeAliveAgain()
|
||||
throws InterruptedException, UnknownHostException, URISyntaxException {
|
||||
GossipSettings settings = new GossipSettings(100, 10000, 1000, 1, 10.0, "normal");
|
||||
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
settings.setPersistRingState(false);
|
||||
settings.setPersistDataState(false);
|
||||
String cluster = UUID.randomUUID().toString();
|
||||
int seedNodes = 3;
|
||||
List<Member> startupMembers = new ArrayList<>();
|
||||
for (int i = 1; i < seedNodes + 1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30300 + i));
|
||||
startupMembers.add(new RemoteMember(cluster, uri, i + ""));
|
||||
}
|
||||
final List<GossipManager> clients = Collections.synchronizedList(new ArrayList<GossipManager>());
|
||||
final int clusterMembers = 5;
|
||||
for (int i = 1; i < clusterMembers + 1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30300 + i));
|
||||
GossipManager gossipService = GossipManagerBuilder.newBuilder()
|
||||
.cluster(cluster)
|
||||
.uri(uri)
|
||||
.id(i + "")
|
||||
.gossipMembers(startupMembers)
|
||||
.gossipSettings(settings)
|
||||
.build();
|
||||
clients.add(gossipService);
|
||||
gossipService.init();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
TUnit.assertThat(new Callable<Integer>() {
|
||||
public Integer call() throws Exception {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}).afterWaitingAtMost(40, TimeUnit.SECONDS).isEqualTo(20);
|
||||
|
||||
// shutdown one client and verify that one client is lost.
|
||||
Random r = new Random();
|
||||
int randomClientId = r.nextInt(clusterMembers);
|
||||
log.info("shutting down " + randomClientId);
|
||||
final int shutdownPort = clients.get(randomClientId).getMyself().getUri()
|
||||
.getPort();
|
||||
final String shutdownId = clients.get(randomClientId).getMyself().getId();
|
||||
clients.get(randomClientId).shutdown();
|
||||
TUnit.assertThat(new Callable<Integer>() {
|
||||
public Integer call() throws Exception {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}).afterWaitingAtMost(40, TimeUnit.SECONDS).isEqualTo(16);
|
||||
clients.remove(randomClientId);
|
||||
|
||||
TUnit.assertThat(new Callable<Integer>() {
|
||||
public Integer call() throws Exception {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers - 1; ++i) {
|
||||
total += clients.get(i).getDeadMembers().size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}).afterWaitingAtMost(50, TimeUnit.SECONDS).isEqualTo(4);
|
||||
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + shutdownPort);
|
||||
// start client again
|
||||
GossipManager gossipService = GossipManagerBuilder.newBuilder()
|
||||
.gossipSettings(settings)
|
||||
.cluster(cluster)
|
||||
.uri(uri)
|
||||
.id(shutdownId+"")
|
||||
.gossipMembers(startupMembers)
|
||||
.build();
|
||||
clients.add(gossipService);
|
||||
gossipService.init();
|
||||
|
||||
// verify that the client is alive again for every node
|
||||
TUnit.assertThat(new Callable<Integer>() {
|
||||
public Integer call() throws Exception {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}).afterWaitingAtMost(60, TimeUnit.SECONDS).isEqualTo(20);
|
||||
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
final int j = i;
|
||||
new Thread() {
|
||||
public void run(){
|
||||
clients.get(j).shutdown();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.apache.gossip.manager.PassiveGossipConstants;
|
||||
import org.apache.gossip.secure.KeyTool;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.teknek.tunit.TUnit;
|
||||
|
||||
public class SignedMessageTest extends AbstractIntegrationBase {
|
||||
|
||||
private GossipSettings gossiperThatSigns(){
|
||||
GossipSettings settings = new GossipSettings();
|
||||
settings.setPersistRingState(false);
|
||||
settings.setPersistDataState(false);
|
||||
settings.setSignMessages(true);
|
||||
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
return settings;
|
||||
}
|
||||
|
||||
private GossipSettings gossiperThatSigns(String keysDir){
|
||||
GossipSettings settings = gossiperThatSigns();
|
||||
settings.setPathToKeyStore(Objects.requireNonNull(keysDir));
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataTest() throws InterruptedException, URISyntaxException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
|
||||
final String keys = System.getProperty("java.io.tmpdir") + "/keys";
|
||||
GossipSettings settings = gossiperThatSigns(keys);
|
||||
setup(keys);
|
||||
String cluster = UUID.randomUUID().toString();
|
||||
List<Member> startupMembers = new ArrayList<>();
|
||||
for (int i = 1; i < 2; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30000 + i));
|
||||
startupMembers.add(new RemoteMember(cluster, uri, i + ""));
|
||||
}
|
||||
final List<GossipManager> clients = new ArrayList<>();
|
||||
for (int i = 1; i < 3; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30000 + i));
|
||||
GossipManager gossipService = GossipManagerBuilder.newBuilder()
|
||||
.cluster(cluster)
|
||||
.uri(uri)
|
||||
.id(i + "")
|
||||
.gossipMembers(startupMembers)
|
||||
.gossipSettings(settings)
|
||||
.build();
|
||||
gossipService.init();
|
||||
clients.add(gossipService);
|
||||
}
|
||||
assertTwoAlive(clients);
|
||||
assertOnlySignedMessages(clients);
|
||||
cleanup(keys, clients);
|
||||
}
|
||||
|
||||
private void assertTwoAlive(List<GossipManager> clients){
|
||||
TUnit.assertThat(() -> {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clients.size(); ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}).afterWaitingAtMost(20, TimeUnit.SECONDS).isEqualTo(2);
|
||||
}
|
||||
|
||||
private void assertOnlySignedMessages(List<GossipManager> clients){
|
||||
Assert.assertEquals(0, clients.get(0).getRegistry()
|
||||
.meter(PassiveGossipConstants.UNSIGNED_MESSAGE).getCount());
|
||||
Assert.assertTrue(clients.get(0).getRegistry()
|
||||
.meter(PassiveGossipConstants.SIGNED_MESSAGE).getCount() > 0);
|
||||
}
|
||||
|
||||
private void cleanup(String keys, List<GossipManager> clients){
|
||||
new File(keys, "1").delete();
|
||||
new File(keys, "2").delete();
|
||||
new File(keys).delete();
|
||||
for (int i = 0; i < clients.size(); ++i) {
|
||||
clients.get(i).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void setup(String keys) throws NoSuchAlgorithmException, NoSuchProviderException, IOException {
|
||||
new File(keys).mkdir();
|
||||
KeyTool.generatePubandPrivateKeyFiles(keys, "1");
|
||||
KeyTool.generatePubandPrivateKeyFiles(keys, "2");
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.UUID;
|
||||
import org.junit.platform.runner.JUnitPlatform;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests support of using {@code StartupSettings} and thereby reading
|
||||
* setup config from file.
|
||||
*/
|
||||
@RunWith(JUnitPlatform.class)
|
||||
public class StartupSettingsTest {
|
||||
private static final Logger log = Logger.getLogger(StartupSettingsTest.class);
|
||||
private static final String CLUSTER = UUID.randomUUID().toString();
|
||||
|
||||
@Test
|
||||
public void testUsingSettingsFile() throws IOException, InterruptedException, URISyntaxException {
|
||||
File settingsFile = File.createTempFile("gossipTest",".json");
|
||||
settingsFile.deleteOnExit();
|
||||
writeSettingsFile(settingsFile);
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + 50000);
|
||||
GossipSettings firstGossipSettings = new GossipSettings();
|
||||
firstGossipSettings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
firstGossipSettings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
GossipManager firstService = GossipManagerBuilder.newBuilder()
|
||||
.cluster(CLUSTER)
|
||||
.uri(uri)
|
||||
.id("1")
|
||||
.gossipSettings(firstGossipSettings).build();
|
||||
firstService.init();
|
||||
GossipManager manager = GossipManagerBuilder.newBuilder()
|
||||
.startupSettings(StartupSettings.fromJSONFile(settingsFile)).build();
|
||||
manager.init();
|
||||
firstService.shutdown();
|
||||
manager.shutdown();
|
||||
}
|
||||
|
||||
private void writeSettingsFile( File target ) throws IOException {
|
||||
String settings =
|
||||
"[{\n" + // It is odd that this is meant to be in an array, but oh well.
|
||||
" \"cluster\":\"" + CLUSTER + "\",\n" +
|
||||
" \"id\":\"" + "2" + "\",\n" +
|
||||
" \"uri\":\"udp://127.0.0.1:50001\",\n" +
|
||||
" \"gossip_interval\":1000,\n" +
|
||||
" \"window_size\":1000,\n" +
|
||||
" \"minimum_samples\":5,\n" +
|
||||
" \"cleanup_interval\":10000,\n" +
|
||||
" \"convict_threshold\":2.6,\n" +
|
||||
" \"distribution\":\"exponential\",\n" +
|
||||
" \"transport_manager_class\":\"org.apache.gossip.transport.UnitTestTransportManager\",\n" +
|
||||
" \"protocol_manager_class\":\"org.apache.gossip.protocol.UnitTestProtocolManager\",\n" +
|
||||
" \"properties\":{},\n" +
|
||||
" \"members\":[\n" +
|
||||
" {\"cluster\": \"" + CLUSTER + "\",\"uri\":\"udp://127.0.0.1:5000\"}\n" +
|
||||
" ]\n" +
|
||||
"}]";
|
||||
|
||||
log.info( "Using settings file with contents of:\n---\n" + settings + "\n---" );
|
||||
FileOutputStream output = new FileOutputStream(target);
|
||||
output.write(settings.getBytes());
|
||||
output.close();
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.gossip;
|
||||
|
||||
import io.teknek.tunit.TUnit;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.platform.runner.JUnitPlatform;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.apache.gossip.manager.GossipManagerBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@RunWith(JUnitPlatform.class)
|
||||
public class TenNodeThreeSeedTest {
|
||||
|
||||
@Test
|
||||
public void test() throws UnknownHostException, InterruptedException, URISyntaxException {
|
||||
abc(30150);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAgain() throws UnknownHostException, InterruptedException, URISyntaxException {
|
||||
abc(30100);
|
||||
}
|
||||
|
||||
public void abc(int base) throws InterruptedException, UnknownHostException, URISyntaxException {
|
||||
GossipSettings settings = new GossipSettings(1000, 10000, 1000, 1, 1.6, "exponential");
|
||||
settings.setPersistRingState(false);
|
||||
settings.setPersistDataState(false);
|
||||
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
|
||||
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
|
||||
String cluster = UUID.randomUUID().toString();
|
||||
int seedNodes = 3;
|
||||
List<Member> startupMembers = new ArrayList<>();
|
||||
for (int i = 1; i < seedNodes+1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
||||
startupMembers.add(new RemoteMember(cluster, uri, i + ""));
|
||||
}
|
||||
final List<GossipManager> clients = new ArrayList<>();
|
||||
final int clusterMembers = 5;
|
||||
for (int i = 1; i < clusterMembers+1; ++i) {
|
||||
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (base + i));
|
||||
GossipManager gossipService = GossipManagerBuilder.newBuilder()
|
||||
.cluster(cluster)
|
||||
.uri(uri)
|
||||
.id(i + "")
|
||||
.gossipSettings(settings)
|
||||
.gossipMembers(startupMembers)
|
||||
.build();
|
||||
gossipService.init();
|
||||
clients.add(gossipService);
|
||||
}
|
||||
TUnit.assertThat(new Callable<Integer> (){
|
||||
public Integer call() throws Exception {
|
||||
int total = 0;
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
total += clients.get(i).getLiveMembers().size();
|
||||
}
|
||||
return total;
|
||||
}}).afterWaitingAtMost(40, TimeUnit.SECONDS).isEqualTo(20);
|
||||
|
||||
for (int i = 0; i < clusterMembers; ++i) {
|
||||
int j = i;
|
||||
new Thread(){
|
||||
public void run(){
|
||||
clients.get(j).shutdown();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
@ -102,7 +102,7 @@ public class MessageHandlerTest {
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void cantAddNullHandler2() {
|
||||
MessageHandler handler = MessageHandlerFactory.concurrentHandler(
|
||||
MessageHandlerFactory.concurrentHandler(
|
||||
new TypedMessageHandler(FakeMessage.class, new FakeMessageHandler()),
|
||||
null,
|
||||
new TypedMessageHandler(FakeMessage.class, new FakeMessageHandler())
|
||||
|
@ -29,7 +29,7 @@ import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/** Only use in unit tests! */
|
||||
public class UnitTestTransportManager extends AbstractTransportManager {
|
||||
public class UnitTestTransportManager extends AbstractTransportManager {
|
||||
|
||||
private static final Map<URI, UnitTestTransportManager> allManagers = new ConcurrentHashMap<>();
|
||||
|
||||
@ -71,6 +71,5 @@ public class UnitTestTransportManager extends AbstractTransportManager {
|
||||
@Override
|
||||
public void startEndpoint() {
|
||||
allManagers.put(localEndpoint, this);
|
||||
super.startEndpoint();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user