blob: cdb138b4eb78022d3a2cf615d85533f9faf183a4 [file] [log] [blame]
Madan Jampania14047d2015-02-25 12:23:02 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
36 // TODO: make these configurable.
37 private static final int WINDOW_SIZE = 250;
38 private static final int MIN_SAMPLES = 25;
Madan Jampania14047d2015-02-25 12:23:02 -080039 private static final double 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.
44 private static final double BOOTSTRAP_PHI_VALUE = 100.0;
45
46 /**
47 * Report a new heart beat for the specified node id.
48 * @param nodeId node id
49 */
50 public void report(NodeId nodeId) {
51 report(nodeId, System.currentTimeMillis());
52 }
53
54 /**
55 * Report a new heart beat for the specified node id.
56 * @param nodeId node id
57 * @param arrivalTime arrival time
58 */
59 public void report(NodeId nodeId, long arrivalTime) {
60 checkNotNull(nodeId, "NodeId must not be null");
61 checkArgument(arrivalTime >= 0, "arrivalTime must not be negative");
62 History nodeState =
63 states.computeIfAbsent(nodeId, key -> new History());
64 synchronized (nodeState) {
65 long latestHeartbeat = nodeState.latestHeartbeatTime();
66 if (latestHeartbeat != -1) {
67 nodeState.samples().addValue(arrivalTime - latestHeartbeat);
68 }
69 nodeState.setLatestHeartbeatTime(arrivalTime);
70 }
71 }
72
73 /**
74 * Compute phi for the specified node id.
75 * @param nodeId node id
76 * @return phi value
77 */
Madan Jampania14047d2015-02-25 12:23:02 -080078 public double phi(NodeId nodeId) {
79 checkNotNull(nodeId, "NodeId must not be null");
Madan Jampanibd6845d2015-02-25 11:43:48 -080080 if (!states.containsKey(nodeId)) {
81 return BOOTSTRAP_PHI_VALUE;
82 }
Madan Jampanibd6845d2015-02-25 11:43:48 -080083 History nodeState = states.get(nodeId);
84 synchronized (nodeState) {
85 long latestHeartbeat = nodeState.latestHeartbeatTime();
86 DescriptiveStatistics samples = nodeState.samples();
87 if (latestHeartbeat == -1 || samples.getN() < MIN_SAMPLES) {
88 return 0.0;
89 }
90 return computePhi(samples, latestHeartbeat, System.currentTimeMillis());
91 }
92 }
93
94 private double computePhi(DescriptiveStatistics samples, long tLast, long tNow) {
95 long size = samples.getN();
96 long t = tNow - tLast;
97 return (size > 0)
Madan Jampania14047d2015-02-25 12:23:02 -080098 ? PHI_FACTOR * t / samples.getMean()
Madan Jampanibd6845d2015-02-25 11:43:48 -080099 : BOOTSTRAP_PHI_VALUE;
100 }
101
102 private static class History {
103 DescriptiveStatistics samples =
104 new DescriptiveStatistics(WINDOW_SIZE);
105 long lastHeartbeatTime = -1;
106
107 public DescriptiveStatistics samples() {
108 return samples;
109 }
110
111 public long latestHeartbeatTime() {
112 return lastHeartbeatTime;
113 }
114
115 public void setLatestHeartbeatTime(long value) {
116 lastHeartbeatTime = value;
117 }
118 }
119}