Yuta HIGUCHI

CopyCat: Dynamic cluster support

Change-Id: I887c52b35811abf37a2b59db034b07ccf01eed2c
...@@ -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;
......
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 +}