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