blob: e573b877a24a2524f810c25d284a75f5c42e3c22 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.cluster.impl;
tom2d7c65f2014-09-23 01:09:35 -070017
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080018import com.google.common.base.MoreObjects;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070019import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Maps;
Madan Jampanie3375062016-04-19 16:32:10 -070021
tom2d7c65f2014-09-23 01:09:35 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
sangyun-hanf98df542016-03-24 20:28:03 +090025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Madan Jampaniafeebbd2015-05-19 15:26:01 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Deepa Vaddireddy6690f592017-03-12 03:31:48 +053029import org.apache.felix.scr.annotations.ReferencePolicy;
tom2d7c65f2014-09-23 01:09:35 -070030import org.apache.felix.scr.annotations.Service;
Madan Jampani7d2fab22015-03-18 17:21:57 -070031import org.joda.time.DateTime;
Ayaka Koshibedd91b842015-03-02 14:48:47 -080032import org.onlab.packet.IpAddress;
33import org.onlab.util.KryoNamespace;
Deepa Vaddireddy6690f592017-03-12 03:31:48 +053034import org.onosproject.cfg.ConfigProperty;
sangyun-han9f0af2d2016-08-04 13:04:59 +090035import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.cluster.ClusterEvent;
Madan Jampaniec1df022015-10-13 21:23:03 -070037import org.onosproject.cluster.ClusterMetadataService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.cluster.ClusterStore;
39import org.onosproject.cluster.ClusterStoreDelegate;
40import org.onosproject.cluster.ControllerNode;
Ayaka Koshibedd91b842015-03-02 14:48:47 -080041import org.onosproject.cluster.ControllerNode.State;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070042import org.onosproject.cluster.DefaultControllerNode;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.cluster.NodeId;
Ayaka Koshibedd91b842015-03-02 14:48:47 -080044import org.onosproject.store.AbstractStore;
Madan Jampanic26eede2015-04-16 11:42:16 -070045import org.onosproject.store.cluster.messaging.Endpoint;
Madan Jampaniafeebbd2015-05-19 15:26:01 -070046import org.onosproject.store.cluster.messaging.MessagingService;
Ayaka Koshibedd91b842015-03-02 14:48:47 -080047import org.onosproject.store.serializers.KryoNamespaces;
HIGUCHI Yutae7290652016-05-18 11:29:01 -070048import org.onosproject.store.serializers.StoreSerializer;
sangyun-hanf98df542016-03-24 20:28:03 +090049import org.osgi.service.component.ComponentContext;
Ayaka Koshibedd91b842015-03-02 14:48:47 -080050import org.slf4j.Logger;
tom2d7c65f2014-09-23 01:09:35 -070051
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070052import java.util.Map;
Thomas Vachuskafba7f3d2016-03-23 15:46:25 -070053import java.util.Objects;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070054import java.util.Set;
55import java.util.concurrent.ExecutorService;
56import java.util.concurrent.Executors;
57import java.util.concurrent.ScheduledExecutorService;
58import java.util.concurrent.TimeUnit;
Madan Jampanid36def02016-01-13 11:21:56 -080059import java.util.function.BiConsumer;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070060import java.util.stream.Collectors;
61
sangyun-hanf98df542016-03-24 20:28:03 +090062import static com.google.common.base.Preconditions.checkArgument;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070063import static com.google.common.base.Preconditions.checkNotNull;
Deepa Vaddireddy6690f592017-03-12 03:31:48 +053064import static com.google.common.base.Strings.isNullOrEmpty;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070065import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskafba7f3d2016-03-23 15:46:25 -070066import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ACTIVATED;
67import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_DEACTIVATED;
68import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_READY;
Jonathan Hart4a4d18f2015-03-26 12:16:16 -070069import static org.slf4j.LoggerFactory.getLogger;
tom2d7c65f2014-09-23 01:09:35 -070070
tom2d7c65f2014-09-23 01:09:35 -070071@Component(immediate = true)
72@Service
Ayaka Koshibedd91b842015-03-02 14:48:47 -080073/**
74 * Distributed cluster nodes store that employs an accrual failure
75 * detector to identify cluster member up/down status.
76 */
tom0755a362014-09-24 11:54:43 -070077public class DistributedClusterStore
Ayaka Koshibedd91b842015-03-02 14:48:47 -080078 extends AbstractStore<ClusterEvent, ClusterStoreDelegate>
tomb41d1ac2014-09-24 01:51:24 -070079 implements ClusterStore {
tom2d7c65f2014-09-23 01:09:35 -070080
Thomas Vachuska8dc1a692015-03-31 01:01:37 -070081 private static final Logger log = getLogger(DistributedClusterStore.class);
tom2d7c65f2014-09-23 01:09:35 -070082
Thomas Vachuskade563cf2015-04-01 00:28:50 -070083 public static final String HEARTBEAT_MESSAGE = "onos-cluster-heartbeat";
84
sangyun-hanf98df542016-03-24 20:28:03 +090085 private static final int DEFAULT_HEARTBEAT_INTERVAL = 100;
86 @Property(name = "heartbeatInterval", intValue = DEFAULT_HEARTBEAT_INTERVAL,
87 label = "Interval time to send heartbeat to other controller nodes (millisecond)")
88 private int heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL;
89
90 private static final int DEFAULT_PHI_FAILURE_THRESHOLD = 10;
91 @Property(name = "phiFailureThreshold", intValue = DEFAULT_PHI_FAILURE_THRESHOLD,
92 label = "the value of Phi threshold to detect accrual failure")
93 private int phiFailureThreshold = DEFAULT_PHI_FAILURE_THRESHOLD;
tom2d7c65f2014-09-23 01:09:35 -070094
HIGUCHI Yutae7290652016-05-18 11:29:01 -070095 private static final StoreSerializer SERIALIZER = StoreSerializer.using(
96 KryoNamespace.newBuilder()
Thomas Vachuska8dc1a692015-03-31 01:01:37 -070097 .register(KryoNamespaces.API)
HIGUCHI Yutae7290652016-05-18 11:29:01 -070098 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
Thomas Vachuska8dc1a692015-03-31 01:01:37 -070099 .register(HeartbeatMessage.class)
HIGUCHI Yutae7290652016-05-18 11:29:01 -0700100 .build("ClusterStore"));
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800101
102 private static final String INSTANCE_ID_NULL = "Instance ID cannot be null";
103
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800104 private final Map<NodeId, ControllerNode> allNodes = Maps.newConcurrentMap();
105 private final Map<NodeId, State> nodeStates = Maps.newConcurrentMap();
Madan Jampani7d2fab22015-03-18 17:21:57 -0700106 private final Map<NodeId, DateTime> nodeStateLastUpdatedTimes = Maps.newConcurrentMap();
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800107
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800108 private ScheduledExecutorService heartBeatSender = Executors.newSingleThreadScheduledExecutor(
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700109 groupedThreads("onos/cluster/membership", "heartbeat-sender", log));
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800110 private ExecutorService heartBeatMessageHandler = Executors.newSingleThreadExecutor(
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700111 groupedThreads("onos/cluster/membership", "heartbeat-receiver", log));
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800112
113 private PhiAccrualFailureDetector failureDetector;
114
115 private ControllerNode localNode;
116
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Madan Jampaniec1df022015-10-13 21:23:03 -0700118 protected ClusterMetadataService clusterMetadataService;
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected MessagingService messagingService;
122
Thomas Vachuska1ca7e9f2016-08-05 10:21:41 -0700123 // This must be optional to avoid a cyclic dependency
Deepa Vaddireddy6690f592017-03-12 03:31:48 +0530124 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
125 bind = "bindComponentConfigService",
126 unbind = "unbindComponentConfigService",
127 policy = ReferencePolicy.DYNAMIC)
sangyun-han9f0af2d2016-08-04 13:04:59 +0900128 protected ComponentConfigService cfgService;
129
Thomas Vachuska1ca7e9f2016-08-05 10:21:41 -0700130 /**
131 * Hook for wiring up optional reference to a service.
132 *
133 * @param service service being announced
134 */
135 protected void bindComponentConfigService(ComponentConfigService service) {
136 if (cfgService == null) {
137 cfgService = service;
138 cfgService.registerProperties(getClass());
Deepa Vaddireddy6690f592017-03-12 03:31:48 +0530139 readComponentConfiguration();
Thomas Vachuska1ca7e9f2016-08-05 10:21:41 -0700140 }
141 }
142
143 /**
144 * Hook for unwiring optional reference to a service.
145 *
146 * @param service service being withdrawn
147 */
148 protected void unbindComponentConfigService(ComponentConfigService service) {
149 if (cfgService == service) {
150 cfgService.unregisterProperties(getClass(), false);
151 cfgService = null;
152 }
153 }
154
tom2d7c65f2014-09-23 01:09:35 -0700155 @Activate
Thomas Vachuska1ca7e9f2016-08-05 10:21:41 -0700156 public void activate() {
Madan Jampaniec1df022015-10-13 21:23:03 -0700157 localNode = clusterMetadataService.getLocalNode();
Thomas Vachuska8dc1a692015-03-31 01:01:37 -0700158
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800159 messagingService.registerHandler(HEARTBEAT_MESSAGE,
Thomas Vachuska8dc1a692015-03-31 01:01:37 -0700160 new HeartbeatMessageHandler(), heartBeatMessageHandler);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800161
162 failureDetector = new PhiAccrualFailureDetector();
163
164 heartBeatSender.scheduleWithFixedDelay(this::heartbeat, 0,
sangyun-hanf98df542016-03-24 20:28:03 +0900165 heartbeatInterval, TimeUnit.MILLISECONDS);
tomb41d1ac2014-09-24 01:51:24 -0700166
167 log.info("Started");
168 }
169
tom2d7c65f2014-09-23 01:09:35 -0700170 @Deactivate
171 public void deactivate() {
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700172 messagingService.unregisterHandler(HEARTBEAT_MESSAGE);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800173 heartBeatSender.shutdownNow();
174 heartBeatMessageHandler.shutdownNow();
175
tom2d7c65f2014-09-23 01:09:35 -0700176 log.info("Stopped");
177 }
178
sangyun-hanf98df542016-03-24 20:28:03 +0900179 @Modified
180 public void modified(ComponentContext context) {
Deepa Vaddireddy6690f592017-03-12 03:31:48 +0530181 readComponentConfiguration();
sangyun-hanf98df542016-03-24 20:28:03 +0900182 }
183
tom2d7c65f2014-09-23 01:09:35 -0700184 @Override
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800185 public void setDelegate(ClusterStoreDelegate delegate) {
186 checkNotNull(delegate, "Delegate cannot be null");
187 this.delegate = delegate;
188 }
189
190 @Override
191 public void unsetDelegate(ClusterStoreDelegate delegate) {
192 this.delegate = null;
193 }
194
195 @Override
196 public boolean hasDelegate() {
197 return this.delegate != null;
198 }
199
200 @Override
tom2d7c65f2014-09-23 01:09:35 -0700201 public ControllerNode getLocalNode() {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800202 return localNode;
tom2d7c65f2014-09-23 01:09:35 -0700203 }
204
205 @Override
206 public Set<ControllerNode> getNodes() {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800207 return ImmutableSet.copyOf(allNodes.values());
tom2d7c65f2014-09-23 01:09:35 -0700208 }
209
210 @Override
211 public ControllerNode getNode(NodeId nodeId) {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800212 checkNotNull(nodeId, INSTANCE_ID_NULL);
213 return allNodes.get(nodeId);
tom2d7c65f2014-09-23 01:09:35 -0700214 }
215
216 @Override
tomb41d1ac2014-09-24 01:51:24 -0700217 public State getState(NodeId nodeId) {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800218 checkNotNull(nodeId, INSTANCE_ID_NULL);
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800219 return MoreObjects.firstNonNull(nodeStates.get(nodeId), State.INACTIVE);
tomb41d1ac2014-09-24 01:51:24 -0700220 }
221
222 @Override
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800223 public void markFullyStarted(boolean started) {
224 updateState(localNode.id(), started ? State.READY : State.ACTIVE);
225 }
226
227 @Override
Pavlin Radoslavov444b5192014-10-28 10:45:19 -0700228 public ControllerNode addNode(NodeId nodeId, IpAddress ip, int tcpPort) {
sangyun-hanf98df542016-03-24 20:28:03 +0900229 checkNotNull(nodeId, INSTANCE_ID_NULL);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800230 ControllerNode node = new DefaultControllerNode(nodeId, ip, tcpPort);
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700231 addNode(node);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800232 return node;
tomee49c372014-09-26 15:14:50 -0700233 }
234
235 @Override
tomb41d1ac2014-09-24 01:51:24 -0700236 public void removeNode(NodeId nodeId) {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800237 checkNotNull(nodeId, INSTANCE_ID_NULL);
238 ControllerNode node = allNodes.remove(nodeId);
239 if (node != null) {
240 nodeStates.remove(nodeId);
Jonathan Hartf1141262015-04-23 11:27:07 -0700241 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_REMOVED, node));
tomb41d1ac2014-09-24 01:51:24 -0700242 }
243 }
244
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700245 private void addNode(ControllerNode node) {
246 allNodes.put(node.id(), node);
Madan Jampaniec1df022015-10-13 21:23:03 -0700247 updateState(node.id(), node.equals(localNode) ? State.ACTIVE : State.INACTIVE);
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700248 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_ADDED, node));
Thomas Vachuskade563cf2015-04-01 00:28:50 -0700249 }
250
Madan Jampani7d2fab22015-03-18 17:21:57 -0700251 private void updateState(NodeId nodeId, State newState) {
Thomas Vachuskafba7f3d2016-03-23 15:46:25 -0700252 State currentState = nodeStates.get(nodeId);
253 if (!Objects.equals(currentState, newState)) {
254 nodeStates.put(nodeId, newState);
255 nodeStateLastUpdatedTimes.put(nodeId, DateTime.now());
256 notifyStateChange(nodeId, currentState, newState);
257 }
Madan Jampani7d2fab22015-03-18 17:21:57 -0700258 }
259
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800260 private void heartbeat() {
261 try {
262 Set<ControllerNode> peers = allNodes.values()
263 .stream()
264 .filter(node -> !(node.id().equals(localNode.id())))
265 .collect(Collectors.toSet());
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800266 State state = nodeStates.get(localNode.id());
Madan Jampanie3375062016-04-19 16:32:10 -0700267 byte[] hbMessagePayload = SERIALIZER.encode(new HeartbeatMessage(localNode, state));
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800268 peers.forEach((node) -> {
269 heartbeatToPeer(hbMessagePayload, node);
270 State currentState = nodeStates.get(node.id());
271 double phi = failureDetector.phi(node.id());
sangyun-hanf98df542016-03-24 20:28:03 +0900272 if (phi >= phiFailureThreshold) {
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800273 if (currentState.isActive()) {
Madan Jampani7d2fab22015-03-18 17:21:57 -0700274 updateState(node.id(), State.INACTIVE);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800275 }
276 } else {
277 if (currentState == State.INACTIVE) {
Madan Jampani7d2fab22015-03-18 17:21:57 -0700278 updateState(node.id(), State.ACTIVE);
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800279 }
280 }
281 });
282 } catch (Exception e) {
283 log.debug("Failed to send heartbeat", e);
284 }
tomb41d1ac2014-09-24 01:51:24 -0700285 }
286
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800287 private void notifyStateChange(NodeId nodeId, State oldState, State newState) {
Thomas Vachuskafba7f3d2016-03-23 15:46:25 -0700288 if (oldState != newState) {
289 ControllerNode node = allNodes.get(nodeId);
Jon Hall1f13d642016-05-13 17:53:01 -0700290 // Either this node or that node is no longer part of the same cluster
291 if (node == null) {
Jon Hall66870baa2016-06-20 12:12:10 -0700292 log.debug("Could not find node {} in the cluster, ignoring state change", nodeId);
Jon Hall1f13d642016-05-13 17:53:01 -0700293 return;
294 }
Thomas Vachuskafba7f3d2016-03-23 15:46:25 -0700295 ClusterEvent.Type type = newState == State.READY ? INSTANCE_READY :
296 newState == State.ACTIVE ? INSTANCE_ACTIVATED :
297 INSTANCE_DEACTIVATED;
298 notifyDelegate(new ClusterEvent(type, node));
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800299 }
tomb41d1ac2014-09-24 01:51:24 -0700300 }
301
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800302 private void heartbeatToPeer(byte[] messagePayload, ControllerNode peer) {
Madan Jampaniafeebbd2015-05-19 15:26:01 -0700303 Endpoint remoteEp = new Endpoint(peer.ip(), peer.tcpPort());
Madan Jampani175e8fd2015-05-20 14:10:45 -0700304 messagingService.sendAsync(remoteEp, HEARTBEAT_MESSAGE, messagePayload).whenComplete((result, error) -> {
305 if (error != null) {
306 log.trace("Sending heartbeat to {} failed", remoteEp, error);
307 }
308 });
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800309 }
310
Madan Jampanid36def02016-01-13 11:21:56 -0800311 private class HeartbeatMessageHandler implements BiConsumer<Endpoint, byte[]> {
tomb41d1ac2014-09-24 01:51:24 -0700312 @Override
Madan Jampanid36def02016-01-13 11:21:56 -0800313 public void accept(Endpoint sender, byte[] message) {
Madan Jampanic26eede2015-04-16 11:42:16 -0700314 HeartbeatMessage hb = SERIALIZER.decode(message);
Madan Jampanie3375062016-04-19 16:32:10 -0700315 if (clusterMetadataService.getClusterMetadata().getNodes().contains(hb.source())) {
316 failureDetector.report(hb.source().id());
317 updateState(hb.source().id(), hb.state);
318 }
tomb41d1ac2014-09-24 01:51:24 -0700319 }
tom2d7c65f2014-09-23 01:09:35 -0700320 }
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800321
322 private static class HeartbeatMessage {
323 private ControllerNode source;
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800324 private State state;
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800325
Madan Jampanie3375062016-04-19 16:32:10 -0700326 public HeartbeatMessage(ControllerNode source, State state) {
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800327 this.source = source;
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800328 this.state = state != null ? state : State.ACTIVE;
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800329 }
330
331 public ControllerNode source() {
332 return source;
333 }
Ayaka Koshibedd91b842015-03-02 14:48:47 -0800334 }
335
Madan Jampani7d2fab22015-03-18 17:21:57 -0700336 @Override
337 public DateTime getLastUpdated(NodeId nodeId) {
338 return nodeStateLastUpdatedTimes.get(nodeId);
339 }
Thomas Vachuskade563cf2015-04-01 00:28:50 -0700340
sangyun-hanf98df542016-03-24 20:28:03 +0900341 /**
Deepa Vaddireddy6690f592017-03-12 03:31:48 +0530342 * Extracts properties from the component configuration.
sangyun-hanf98df542016-03-24 20:28:03 +0900343 *
sangyun-hanf98df542016-03-24 20:28:03 +0900344 */
Deepa Vaddireddy6690f592017-03-12 03:31:48 +0530345 private void readComponentConfiguration() {
346 Set<ConfigProperty> configProperties = cfgService.getProperties(getClass().getName());
347 for (ConfigProperty property : configProperties) {
348 if (property.name().equals("heartbeatInterval")) {
349 String s = property.value();
350 if (s == null) {
351 setHeartbeatInterval(DEFAULT_HEARTBEAT_INTERVAL);
352 log.info("Heartbeat interval time is not configured, default value is {}",
353 DEFAULT_HEARTBEAT_INTERVAL);
354 } else {
355 int newHeartbeatInterval = isNullOrEmpty(s) ? DEFAULT_HEARTBEAT_INTERVAL
356 : Integer.parseInt(s.trim());
357 if (newHeartbeatInterval > 0 && heartbeatInterval != newHeartbeatInterval) {
358 heartbeatInterval = newHeartbeatInterval;
359 restartHeartbeatSender();
360 }
361 log.info("Configured. Heartbeat interval time is configured to {}",
362 heartbeatInterval);
363 }
364 }
365 if (property.name().equals("phiFailureThreshold")) {
366 String s = property.value();
367 if (s == null) {
368 setPhiFailureThreshold(DEFAULT_PHI_FAILURE_THRESHOLD);
369 log.info("Phi failure threshold is not configured, default value is {}",
370 DEFAULT_PHI_FAILURE_THRESHOLD);
371 } else {
372 int newPhiFailureThreshold = isNullOrEmpty(s) ? DEFAULT_HEARTBEAT_INTERVAL
373 : Integer.parseInt(s.trim());
374 setPhiFailureThreshold(newPhiFailureThreshold);
375 log.info("Configured. Phi failure threshold is configured to {}",
376 phiFailureThreshold);
377 }
378 }
sangyun-hanf98df542016-03-24 20:28:03 +0900379 }
380 }
381
382 /**
383 * Sets heartbeat interval between the termination of one execution of heartbeat
384 * and the commencement of the next.
385 *
386 * @param interval term between each heartbeat
387 */
388 private void setHeartbeatInterval(int interval) {
389 try {
390 checkArgument(interval > 0, "Interval must be greater than zero");
391 heartbeatInterval = interval;
392 } catch (IllegalArgumentException e) {
393 log.warn(e.getMessage());
394 heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL;
395 }
396 }
397
398 /**
399 * Sets Phi failure threshold.
400 * Phi is based on a paper titled: "The φ Accrual Failure Detector" by Hayashibara, et al.
401 *
402 * @param threshold
403 */
404 private void setPhiFailureThreshold(int threshold) {
405 phiFailureThreshold = threshold;
406 }
407
408 /**
409 * Restarts heartbeatSender executor.
sangyun-hanf98df542016-03-24 20:28:03 +0900410 */
411 private void restartHeartbeatSender() {
412 try {
413 ScheduledExecutorService prevSender = heartBeatSender;
414 heartBeatSender = Executors.newSingleThreadScheduledExecutor(
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700415 groupedThreads("onos/cluster/membership", "heartbeat-sender-%d", log));
sangyun-hanf98df542016-03-24 20:28:03 +0900416 heartBeatSender.scheduleWithFixedDelay(this::heartbeat, 0,
417 heartbeatInterval, TimeUnit.MILLISECONDS);
418 prevSender.shutdown();
419 } catch (Exception e) {
420 log.warn(e.getMessage());
421 }
422 }
Jon Hall1f13d642016-05-13 17:53:01 -0700423}