aboutsummaryrefslogtreecommitdiffstats
path: root/zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LatencyStatsTest.java
blob: 12607aeca92c38e26220636050b7ae5df8a867e9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.curator.stats;

import com.yahoo.vespa.curator.stats.LatencyStats.ActiveInterval;
import org.junit.Test;

import java.util.function.LongSupplier;

import static org.junit.Assert.assertEquals;

public class LatencyStatsTest {
    private final NanoTimeSupplier nanoTimeSupplier = new NanoTimeSupplier();
    private final LatencyStats stats = new LatencyStats(nanoTimeSupplier);

    @Test
    public void defaults() {
        assertNoActivity(stats.getLatencyMetrics());
        assertNoActivity(stats.getLatencyMetricsAndStartNewPeriod());
        assertNoActivity(stats.getLatencyMetricsAndStartNewPeriod());
    }

    @Test
    public void oneInterval() {
        ActiveInterval activeInterval = stats.startNewInterval();

        int micros = 1_234_567;
        nanoTimeSupplier.addMicros(micros);
        activeInterval.close();

        nanoTimeSupplier.addMicros(32_000_000 - micros);
        var latencyMetrics = stats.getLatencyMetricsAndStartNewPeriod();
        // 1.234567 gets truncated to 1.234 (rounding to 1.235 would also be fine)
        assertDoubleEquals(1.234f, latencyMetrics.latencySeconds());
        assertDoubleEquals(1.234f, latencyMetrics.maxLatencySeconds());
        assertDoubleEquals(1.234f, latencyMetrics.maxActiveLatencySeconds());
        // 1 / 32 = 0.03125
        assertDoubleEquals(0.031f, latencyMetrics.startHz());
        assertDoubleEquals(0.031f, latencyMetrics.endHz());
        // 1.234567 / 32 rounded to 0.039 (truncating to 0.038 would also be fine)
        assertDoubleEquals(0.039f, latencyMetrics.load());
        assertEquals(1, latencyMetrics.maxLoad());
        assertEquals(0, latencyMetrics.currentLoad());

        assertNoActivity();
    }

    @Test
    public void manyIntervals() {
        nanoTimeSupplier.addSeconds(1);
        ActiveInterval activeInterval1 = stats.startNewInterval();
        nanoTimeSupplier.addSeconds(1);
        ActiveInterval activeInterval2 = stats.startNewInterval();

        assertLatencyMetrics(
                0.0,  // latency: No intervals have ended
                0.0,  // maxLatency: No intervals have ended
                1.0,  // maxActiveLatency:  First interval has lasted 1s
                1.0,  // startHz:  There have been 2 starts in 2 seconds
                0.0,  // endHz:  There have been 0 endings of intervals
                0.5,  // load:  First second had 0 active intervals, second second had 1.
                2,  // maxLoad:  There are now 2 active intervals
                2);  // currentLoad:  There are now 2 active intervals

        nanoTimeSupplier.addSeconds(1);
        ActiveInterval activeInterval3 = stats.startNewInterval();

        nanoTimeSupplier.addSeconds(1);
        activeInterval1.close();
        activeInterval3.close();

        assertLatencyMetrics(
                2.0,  // latency: 2 intervals ended: 3s and 1s
                3.0,
                3.0,  // maxActiveLatency:  both interval 1 and 2 have 3s latency
                0.75,  // startHz: 3 started in 4s
                0.5,
                1.5,  // load: 1s of each of 0, 1, 2, and 3 active intervals.
                3,
                1);
    }

    @Test
    public void intervalsCrossingPeriods() {
        nanoTimeSupplier.addSeconds(1);
        ActiveInterval activeInterval1 = stats.startNewInterval();
        nanoTimeSupplier.addSeconds(1);

        stats.getLatencyMetricsAndStartNewPeriod();
        assertLatencyMetrics(
                0, 0, 1,  // maxActiveLatency:  One active interval has latency 1s
                0, 0,
                1, 1, 1);  // all loads are 1

        nanoTimeSupplier.addSeconds(1);
        activeInterval1.close();
        assertLatencyMetrics(
                2, 2, 2,
                0, 1,  // startHz, endHz
                1, 1, 0);  // currentLoad just dropped to 0
    }

    private void assertLatencyMetrics(double latencySeconds, double maxLatencySeconds, double maxActiveLatencySeconds,
                                      double startHz, double endHz,
                                      double load, int maxLoad, int currentLoad) {
        var latencyMetrics = stats.getLatencyMetrics();
        assertDoubleEquals(latencySeconds, latencyMetrics.latencySeconds());
        assertDoubleEquals(maxLatencySeconds, latencyMetrics.maxLatencySeconds());
        assertDoubleEquals(maxActiveLatencySeconds, latencyMetrics.maxActiveLatencySeconds());
        assertDoubleEquals(startHz, latencyMetrics.startHz());
        assertDoubleEquals(endHz, latencyMetrics.endHz());
        assertDoubleEquals(load, latencyMetrics.load());
        assertEquals(maxLoad, latencyMetrics.maxLoad());
        assertEquals(currentLoad, latencyMetrics.currentLoad());
    }

    private void assertNoActivity() { assertNoActivity(stats.getLatencyMetricsAndStartNewPeriod()); }

    private void assertNoActivity(LatencyMetrics latencyMetrics) {
        assertDoubleEquals(0.0, latencyMetrics.latencySeconds());
        assertDoubleEquals(0.0, latencyMetrics.maxLatencySeconds());
        assertDoubleEquals(0.0, latencyMetrics.maxActiveLatencySeconds());
        assertDoubleEquals(0.0, latencyMetrics.startHz());
        assertDoubleEquals(0.0, latencyMetrics.endHz());
        assertDoubleEquals(0.0, latencyMetrics.load());
        assertEquals(0, latencyMetrics.maxLoad());
        assertEquals(0, latencyMetrics.currentLoad());
    }

    private void assertDoubleEquals(double expected, double actual) {
        assertEquals(expected, actual, 1e-5);
    }

    private static class NanoTimeSupplier implements LongSupplier {
        // The initial nano time should not matter
        private long nanoTime = 0x678abf4967L;

        public void addSeconds(int seconds) { nanoTime += seconds * 1_000_000_000L; }
        public void addMillis(int millis) { nanoTime += millis * 1_000_000L; }
        public void addMicros(int micros) { nanoTime += micros * 1_000L; }
        public void addNanos(int nanos) { nanoTime += nanos; }

        @Override
        public long getAsLong() { return nanoTime; }
    }
}