-
Notifications
You must be signed in to change notification settings - Fork 997
/
log_test.go
158 lines (135 loc) · 3.12 KB
/
log_test.go
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
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package raft
import (
"bytes"
"fmt"
"testing"
"time"
metrics "github.com/armon/go-metrics"
)
func TestOldestLog(t *testing.T) {
cases := []struct {
Name string
Logs []*Log
WantIdx uint64
WantErr bool
}{
{
Name: "empty logs",
Logs: nil,
WantIdx: 0,
WantErr: true,
},
{
Name: "simple case",
Logs: []*Log{
{
Index: 1234,
Term: 1,
},
{
Index: 1235,
Term: 1,
},
{
Index: 1236,
Term: 2,
},
},
WantIdx: 1234,
WantErr: false,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
s := NewInmemStore()
if err := s.StoreLogs(tc.Logs); err != nil {
t.Fatalf("expected store logs not to fail: %s", err)
}
got, err := oldestLog(s)
switch {
case tc.WantErr && err == nil:
t.Fatalf("wanted error got nil")
case !tc.WantErr && err != nil:
t.Fatalf("wanted no error got: %s", err)
}
if got.Index != tc.WantIdx {
t.Fatalf("got index %v, want %v", got.Index, tc.WantIdx)
}
})
}
}
func TestEmitsLogStoreMetrics(t *testing.T) {
sink := testSetupMetrics(t)
start := time.Now()
s := NewInmemStore()
logs := []*Log{
{
Index: 1234,
Term: 1,
AppendedAt: time.Now(),
},
{
Index: 1235,
Term: 1,
},
{
Index: 1236,
Term: 2,
},
}
if err := s.StoreLogs(logs); err != nil {
t.Fatalf("expected store logs not to fail: %s", err)
}
stopCh := make(chan struct{})
defer close(stopCh)
go emitLogStoreMetrics(s, []string{"foo"}, time.Millisecond, stopCh)
// Wait for at least one interval
time.Sleep(5 * time.Millisecond)
got := getCurrentGaugeValue(t, sink, "raft.test.foo.oldestLogAge")
// Assert the age is in a reasonable range.
if got > float32(time.Since(start).Milliseconds()) {
t.Fatalf("max age before test start: %v", got)
}
if got < 1 {
t.Fatalf("max age less than interval: %v", got)
}
}
func testSetupMetrics(t *testing.T) *metrics.InmemSink {
// Record for ages (5 mins) so we can be confident that our assertions won't
// fail on silly long test runs due to dropped data.
s := metrics.NewInmemSink(10*time.Second, 300*time.Second)
cfg := metrics.DefaultConfig("raft.test")
cfg.EnableHostname = false
metrics.NewGlobal(cfg, s)
return s
}
func getCurrentGaugeValue(t *testing.T, sink *metrics.InmemSink, name string) float32 {
t.Helper()
data := sink.Data()
// Loop backward through intervals until there is a non-empty one
// Addresses flakiness around recording to one interval but accessing during the next
for i := len(data) - 1; i >= 0; i-- {
currentInterval := data[i]
currentInterval.RLock()
if gv, ok := currentInterval.Gauges[name]; ok {
currentInterval.RUnlock()
return gv.Value
}
currentInterval.RUnlock()
}
// Debug print all the gauges
buf := bytes.NewBuffer(nil)
for _, intv := range data {
intv.RLock()
for name, val := range intv.Gauges {
fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value)
}
intv.RUnlock()
}
t.Log(buf.String())
t.Fatalf("didn't find gauge %q", name)
return 0
}