CopyCat: Dynamic cluster support
Change-Id: I887c52b35811abf37a2b59db034b07ccf01eed2c
Showing
3 changed files
with
120 additions
and
14 deletions
| ... | @@ -8,17 +8,21 @@ import java.util.List; | ... | @@ -8,17 +8,21 @@ import java.util.List; |
| 8 | 8 | ||
| 9 | import net.kuujo.copycat.Copycat; | 9 | import net.kuujo.copycat.Copycat; |
| 10 | import net.kuujo.copycat.StateMachine; | 10 | import net.kuujo.copycat.StateMachine; |
| 11 | +import net.kuujo.copycat.cluster.ClusterConfig; | ||
| 11 | import net.kuujo.copycat.cluster.TcpCluster; | 12 | import net.kuujo.copycat.cluster.TcpCluster; |
| 12 | import net.kuujo.copycat.cluster.TcpClusterConfig; | 13 | import net.kuujo.copycat.cluster.TcpClusterConfig; |
| 13 | import net.kuujo.copycat.cluster.TcpMember; | 14 | import net.kuujo.copycat.cluster.TcpMember; |
| 14 | import net.kuujo.copycat.log.InMemoryLog; | 15 | import net.kuujo.copycat.log.InMemoryLog; |
| 15 | import net.kuujo.copycat.log.Log; | 16 | import net.kuujo.copycat.log.Log; |
| 17 | + | ||
| 16 | import org.apache.felix.scr.annotations.Activate; | 18 | import org.apache.felix.scr.annotations.Activate; |
| 17 | import org.apache.felix.scr.annotations.Component; | 19 | import org.apache.felix.scr.annotations.Component; |
| 18 | import org.apache.felix.scr.annotations.Deactivate; | 20 | import org.apache.felix.scr.annotations.Deactivate; |
| 19 | import org.apache.felix.scr.annotations.Reference; | 21 | import org.apache.felix.scr.annotations.Reference; |
| 20 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 21 | import org.apache.felix.scr.annotations.Service; | 23 | import org.apache.felix.scr.annotations.Service; |
| 24 | +import org.onlab.onos.cluster.ClusterEvent; | ||
| 25 | +import org.onlab.onos.cluster.ClusterEventListener; | ||
| 22 | import org.onlab.onos.cluster.ClusterService; | 26 | import org.onlab.onos.cluster.ClusterService; |
| 23 | import org.onlab.onos.cluster.ControllerNode; | 27 | import org.onlab.onos.cluster.ControllerNode; |
| 24 | import org.onlab.onos.store.service.DatabaseAdminService; | 28 | import org.onlab.onos.store.service.DatabaseAdminService; |
| ... | @@ -35,8 +39,6 @@ import org.onlab.onos.store.service.WriteRequest; | ... | @@ -35,8 +39,6 @@ import org.onlab.onos.store.service.WriteRequest; |
| 35 | import org.onlab.onos.store.service.WriteResult; | 39 | import org.onlab.onos.store.service.WriteResult; |
| 36 | import org.slf4j.Logger; | 40 | import org.slf4j.Logger; |
| 37 | 41 | ||
| 38 | -import com.google.common.collect.Lists; | ||
| 39 | - | ||
| 40 | /** | 42 | /** |
| 41 | * Strongly consistent and durable state management service based on | 43 | * Strongly consistent and durable state management service based on |
| 42 | * Copycat implementation of Raft consensus protocol. | 44 | * Copycat implementation of Raft consensus protocol. |
| ... | @@ -58,17 +60,29 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -58,17 +60,29 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 58 | private Copycat copycat; | 60 | private Copycat copycat; |
| 59 | private DatabaseClient client; | 61 | private DatabaseClient client; |
| 60 | 62 | ||
| 63 | + // TODO: check if synchronization is required to read/modify this | ||
| 64 | + private ClusterConfig<TcpMember> clusterConfig; | ||
| 65 | + | ||
| 66 | + private ClusterEventListener clusterEventListener; | ||
| 67 | + | ||
| 61 | @Activate | 68 | @Activate |
| 62 | public void activate() { | 69 | public void activate() { |
| 63 | - log.info("Starting."); | ||
| 64 | 70 | ||
| 65 | - // TODO: Not every node can be part of the consensus ring. | 71 | + // TODO: Not every node should be part of the consensus ring. |
| 66 | 72 | ||
| 73 | + final ControllerNode localNode = clusterService.getLocalNode(); | ||
| 67 | TcpMember localMember = | 74 | TcpMember localMember = |
| 68 | new TcpMember( | 75 | new TcpMember( |
| 69 | - clusterService.getLocalNode().ip().toString(), | 76 | + localNode.ip().toString(), |
| 70 | - clusterService.getLocalNode().tcpPort()); | 77 | + localNode.tcpPort()); |
| 71 | - List<TcpMember> remoteMembers = Lists.newArrayList(); | 78 | + |
| 79 | + clusterConfig = new TcpClusterConfig(); | ||
| 80 | + clusterConfig.setLocalMember(localMember); | ||
| 81 | + | ||
| 82 | + List<TcpMember> remoteMembers = new ArrayList<>(clusterService.getNodes().size()); | ||
| 83 | + | ||
| 84 | + clusterEventListener = new InternalClusterEventListener(); | ||
| 85 | + clusterService.addListener(clusterEventListener); | ||
| 72 | 86 | ||
| 73 | for (ControllerNode node : clusterService.getNodes()) { | 87 | for (ControllerNode node : clusterService.getNodes()) { |
| 74 | TcpMember member = new TcpMember(node.ip().toString(), node.tcpPort()); | 88 | TcpMember member = new TcpMember(node.ip().toString(), node.tcpPort()); |
| ... | @@ -76,21 +90,18 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -76,21 +90,18 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 76 | remoteMembers.add(member); | 90 | remoteMembers.add(member); |
| 77 | } | 91 | } |
| 78 | } | 92 | } |
| 93 | + clusterConfig.addRemoteMembers(remoteMembers); | ||
| 79 | 94 | ||
| 80 | - // Configure the cluster. | 95 | + log.info("Starting cluster with Local:[{}], Remote:{}", localMember, remoteMembers); |
| 81 | - TcpClusterConfig config = new TcpClusterConfig(); | ||
| 82 | 96 | ||
| 83 | - config.setLocalMember(localMember); | ||
| 84 | - config.setRemoteMembers(remoteMembers.toArray(new TcpMember[]{})); | ||
| 85 | 97 | ||
| 86 | // Create the cluster. | 98 | // Create the cluster. |
| 87 | - TcpCluster cluster = new TcpCluster(config); | 99 | + TcpCluster cluster = new TcpCluster(clusterConfig); |
| 88 | 100 | ||
| 89 | StateMachine stateMachine = new DatabaseStateMachine(); | 101 | StateMachine stateMachine = new DatabaseStateMachine(); |
| 90 | - ControllerNode thisNode = clusterService.getLocalNode(); | ||
| 91 | // FIXME resolve Chronicle + OSGi issue | 102 | // FIXME resolve Chronicle + OSGi issue |
| 92 | //Log consensusLog = new ChronicleLog(LOG_FILE_PREFIX + "_" + thisNode.id()); | 103 | //Log consensusLog = new ChronicleLog(LOG_FILE_PREFIX + "_" + thisNode.id()); |
| 93 | - Log consensusLog = new InMemoryLog(); | 104 | + Log consensusLog = new KryoRegisteredInMemoryLog(); |
| 94 | 105 | ||
| 95 | copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol); | 106 | copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol); |
| 96 | copycat.start(); | 107 | copycat.start(); |
| ... | @@ -102,6 +113,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -102,6 +113,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 102 | 113 | ||
| 103 | @Deactivate | 114 | @Deactivate |
| 104 | public void deactivate() { | 115 | public void deactivate() { |
| 116 | + clusterService.removeListener(clusterEventListener); | ||
| 105 | copycat.stop(); | 117 | copycat.stop(); |
| 106 | log.info("Stopped."); | 118 | log.info("Stopped."); |
| 107 | } | 119 | } |
| ... | @@ -179,6 +191,46 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -179,6 +191,46 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 179 | 191 | ||
| 180 | } | 192 | } |
| 181 | 193 | ||
| 194 | + private final class InternalClusterEventListener | ||
| 195 | + implements ClusterEventListener { | ||
| 196 | + | ||
| 197 | + @Override | ||
| 198 | + public void event(ClusterEvent event) { | ||
| 199 | + // TODO: Not every node should be part of the consensus ring. | ||
| 200 | + | ||
| 201 | + final ControllerNode node = event.subject(); | ||
| 202 | + final TcpMember tcpMember = new TcpMember(node.ip().toString(), | ||
| 203 | + node.tcpPort()); | ||
| 204 | + | ||
| 205 | + log.trace("{}", event); | ||
| 206 | + switch (event.type()) { | ||
| 207 | + case INSTANCE_ACTIVATED: | ||
| 208 | + case INSTANCE_ADDED: | ||
| 209 | + log.info("{} was added to the cluster", tcpMember); | ||
| 210 | + clusterConfig.addRemoteMember(tcpMember); | ||
| 211 | + break; | ||
| 212 | + case INSTANCE_DEACTIVATED: | ||
| 213 | + case INSTANCE_REMOVED: | ||
| 214 | + log.info("{} was removed from the cluster", tcpMember); | ||
| 215 | + clusterConfig.removeRemoteMember(tcpMember); | ||
| 216 | + break; | ||
| 217 | + default: | ||
| 218 | + break; | ||
| 219 | + } | ||
| 220 | + log.info("Current cluster: {}", clusterConfig.getMembers()); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + public static final class KryoRegisteredInMemoryLog extends InMemoryLog { | ||
| 226 | + public KryoRegisteredInMemoryLog() { | ||
| 227 | + super(); | ||
| 228 | + // required to deserialize object across bundles | ||
| 229 | + super.kryo.register(TcpMember.class, new TcpMemberSerializer()); | ||
| 230 | + super.kryo.register(TcpClusterConfig.class, new TcpClusterConfigSerializer()); | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + | ||
| 182 | private class DatabaseOperationResult<R, E extends DatabaseException> implements OptionalResult<R, E> { | 234 | private class DatabaseOperationResult<R, E extends DatabaseException> implements OptionalResult<R, E> { |
| 183 | 235 | ||
| 184 | private final R result; | 236 | private final R result; | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/service/impl/TcpClusterConfigSerializer.java
0 → 100644
| 1 | +package org.onlab.onos.store.service.impl; | ||
| 2 | + | ||
| 3 | +import java.util.Collection; | ||
| 4 | + | ||
| 5 | +import net.kuujo.copycat.cluster.TcpClusterConfig; | ||
| 6 | +import net.kuujo.copycat.cluster.TcpMember; | ||
| 7 | + | ||
| 8 | +import com.esotericsoftware.kryo.Kryo; | ||
| 9 | +import com.esotericsoftware.kryo.Serializer; | ||
| 10 | +import com.esotericsoftware.kryo.io.Input; | ||
| 11 | +import com.esotericsoftware.kryo.io.Output; | ||
| 12 | + | ||
| 13 | +public class TcpClusterConfigSerializer extends Serializer<TcpClusterConfig> { | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public void write(Kryo kryo, Output output, TcpClusterConfig object) { | ||
| 17 | + kryo.writeClassAndObject(output, object.getLocalMember()); | ||
| 18 | + kryo.writeClassAndObject(output, object.getRemoteMembers()); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public TcpClusterConfig read(Kryo kryo, Input input, | ||
| 23 | + Class<TcpClusterConfig> type) { | ||
| 24 | + TcpMember localMember = (TcpMember) kryo.readClassAndObject(input); | ||
| 25 | + @SuppressWarnings("unchecked") | ||
| 26 | + Collection<TcpMember> remoteMembers = (Collection<TcpMember>) kryo.readClassAndObject(input); | ||
| 27 | + return new TcpClusterConfig(localMember, remoteMembers); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | +} |
| 1 | +package org.onlab.onos.store.service.impl; | ||
| 2 | + | ||
| 3 | +import net.kuujo.copycat.cluster.TcpMember; | ||
| 4 | + | ||
| 5 | +import com.esotericsoftware.kryo.Kryo; | ||
| 6 | +import com.esotericsoftware.kryo.Serializer; | ||
| 7 | +import com.esotericsoftware.kryo.io.Input; | ||
| 8 | +import com.esotericsoftware.kryo.io.Output; | ||
| 9 | + | ||
| 10 | +public class TcpMemberSerializer extends Serializer<TcpMember> { | ||
| 11 | + | ||
| 12 | + @Override | ||
| 13 | + public void write(Kryo kryo, Output output, TcpMember object) { | ||
| 14 | + output.writeString(object.host()); | ||
| 15 | + output.writeInt(object.port()); | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public TcpMember read(Kryo kryo, Input input, Class<TcpMember> type) { | ||
| 20 | + String host = input.readString(); | ||
| 21 | + int port = input.readInt(); | ||
| 22 | + return new TcpMember(host, port); | ||
| 23 | + } | ||
| 24 | +} |
-
Please register or login to post a comment