blob: b80142bcb04ffbce6195bc3e081e32b3fc7f6ab3 [file] [log] [blame]
Madan Jampania14047d2015-02-25 12:23:02 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Madan Jampania14047d2015-02-25 12:23:02 -08003 *
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 */
Madan Jampanibd6845d2015-02-25 11:43:48 -080016package org.onosproject.store.cluster.impl;
17
18import static com.google.common.base.Preconditions.checkArgument;
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import java.util.Map;
22
23import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
24import org.onosproject.cluster.NodeId;
25
26import com.google.common.collect.Maps;
27
28/**
29 * Phi Accrual failure detector.
30 * <p>
31 * Based on a paper titled: "The φ Accrual Failure Detector" by Hayashibara, et al.
32 */
33public class PhiAccrualFailureDetector {
34 private final Map<NodeId, History> states = Maps.newConcurrentMap();
35
sangyun-hanf98df542016-03-24 20:28:03 +090036 // Default value
37 private static final int DEFAULT_WINDOW_SIZE = 250;
38 private static final int DEFAULT_MIN_SAMPLES = 25;
39 private static final double DEFAULT_PHI_FACTOR = 1.0 / Math.log(10.0);
Madan Jampanibd6845d2015-02-25 11:43:48 -080040
41 // If a node does not have any heartbeats, this is the phi
42 // value to report. Indicates the node is inactive (from the
43 // detectors perspective.
sangyun-hanf98df542016-03-24 20:28:03 +090044 private static final double DEFAULT_BOOTSTRAP_PHI_VALUE = 100.0;
45
46
47 private int minSamples = DEFAULT_MIN_SAMPLES;
48 private double phiFactor = DEFAULT_PHI_FACTOR;
49 private double bootstrapPhiValue = DEFAULT_BOOTSTRAP_PHI_VALUE;
Madan Jampanibd6845d2015-02-25 11:43:48 -080050
51 /**
Jordan Haltermane7062532018-01-22 11:22:46 -080052 * Returns the last heartbeat time for the given node.
53 *
54 * @param nodeId the node identifier
55 * @return the last heartbeat time for the given node
56 */
57 public long getLastHeartbeatTime(NodeId nodeId) {
58 History nodeState = states.computeIfAbsent(nodeId, key -> new History());
59 return nodeState.latestHeartbeatTime();
60 }
61
62 /**
Madan Jampanibd6845d2015-02-25 11:43:48 -080063 * Report a new heart beat for the specified node id.
64 * @param nodeId node id
65 */
66 public void report(NodeId nodeId) {
67 report(nodeId, System.currentTimeMillis());
68 }
69
70 /**
71 * Report a new heart beat for the specified node id.
72 * @param nodeId node id
73 * @param arrivalTime arrival time
74 */
75 public void report(NodeId nodeId, long arrivalTime) {
76 checkNotNull(nodeId, "NodeId must not be null");
77 checkArgument(arrivalTime >= 0, "arrivalTime must not be negative");
78 History nodeState =
79 states.computeIfAbsent(nodeId, key -> new History());
80 synchronized (nodeState) {
81 long latestHeartbeat = nodeState.latestHeartbeatTime();
82 if (latestHeartbeat != -1) {
83 nodeState.samples().addValue(arrivalTime - latestHeartbeat);
84 }
85 nodeState.setLatestHeartbeatTime(arrivalTime);
86 }
87 }
88
Jordan Halterman1315d3e2018-01-16 19:41:31 -080089 /**
90 * Resets the failure detector for the given node.
91 *
92 * @param nodeId node identifier for the node for which to reset the failure detector
93 */
94 public void reset(NodeId nodeId) {
Jordan Haltermane7062532018-01-22 11:22:46 -080095 states.remove(nodeId);
Jordan Halterman1315d3e2018-01-16 19:41:31 -080096 }
sangyun-hanf98df542016-03-24 20:28:03 +090097
Madan Jampanibd6845d2015-02-25 11:43:48 -080098 /**
99 * Compute phi for the specified node id.
100 * @param nodeId node id
101 * @return phi value
102 */
Madan Jampania14047d2015-02-25 12:23:02 -0800103 public double phi(NodeId nodeId) {
104 checkNotNull(nodeId, "NodeId must not be null");
Madan Jampanibd6845d2015-02-25 11:43:48 -0800105 if (!states.containsKey(nodeId)) {
sangyun-hanf98df542016-03-24 20:28:03 +0900106 return bootstrapPhiValue;
Madan Jampanibd6845d2015-02-25 11:43:48 -0800107 }
Madan Jampanibd6845d2015-02-25 11:43:48 -0800108 History nodeState = states.get(nodeId);
109 synchronized (nodeState) {
110 long latestHeartbeat = nodeState.latestHeartbeatTime();
111 DescriptiveStatistics samples = nodeState.samples();
sangyun-hanf98df542016-03-24 20:28:03 +0900112 if (latestHeartbeat == -1 || samples.getN() < minSamples) {
Madan Jampanibd6845d2015-02-25 11:43:48 -0800113 return 0.0;
114 }
115 return computePhi(samples, latestHeartbeat, System.currentTimeMillis());
116 }
117 }
118
119 private double computePhi(DescriptiveStatistics samples, long tLast, long tNow) {
120 long size = samples.getN();
121 long t = tNow - tLast;
122 return (size > 0)
sangyun-hanf98df542016-03-24 20:28:03 +0900123 ? phiFactor * t / samples.getMean()
124 : bootstrapPhiValue;
Madan Jampanibd6845d2015-02-25 11:43:48 -0800125 }
126
sangyun-hanf98df542016-03-24 20:28:03 +0900127
128 private void setMinSamples(int samples) {
129 minSamples = samples;
130 }
131
132 private void setPhiFactor(double factor) {
133 phiFactor = factor;
134 }
135
136 private void setBootstrapPhiValue(double phiValue) {
137 bootstrapPhiValue = phiValue;
138 }
139
140
Madan Jampanibd6845d2015-02-25 11:43:48 -0800141 private static class History {
142 DescriptiveStatistics samples =
sangyun-hanf98df542016-03-24 20:28:03 +0900143 new DescriptiveStatistics(DEFAULT_WINDOW_SIZE);
Madan Jampanibd6845d2015-02-25 11:43:48 -0800144 long lastHeartbeatTime = -1;
145
146 public DescriptiveStatistics samples() {
147 return samples;
148 }
149
150 public long latestHeartbeatTime() {
151 return lastHeartbeatTime;
152 }
153
154 public void setLatestHeartbeatTime(long value) {
155 lastHeartbeatTime = value;
156 }
157 }
158}