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
|
package statsengine
import (
"fmt"
"reflect"
"testing"
"ior/internal/event"
"ior/internal/file"
"ior/internal/types"
)
func TestFileRankerHeapEviction(t *testing.T) {
r := newFileRankerWithConfig(2)
r.Add(newFilePair("/a", 10, 1, types.READ_CLASSIFIED))
r.Add(newFilePair("/b", 10, 1, types.READ_CLASSIFIED))
r.Add(newFilePair("/b", 10, 1, types.READ_CLASSIFIED))
r.Add(newFilePair("/c", 10, 1, types.READ_CLASSIFIED))
r.Add(newFilePair("/c", 10, 1, types.READ_CLASSIFIED))
r.Add(newFilePair("/c", 10, 1, types.READ_CLASSIFIED))
got := paths(r.Snapshot())
want := []string{"/c", "/b"}
if !reflect.DeepEqual(got, want) {
t.Fatalf("unexpected top-n paths: got %v want %v", got, want)
}
}
func TestFileRankerRankingCorrectness(t *testing.T) {
r := newFileRankerWithConfig(3)
r.Add(newFilePair("/read", 20, 11, types.READ_CLASSIFIED))
r.Add(newFilePair("/read", 10, 9, types.READ_CLASSIFIED))
r.Add(newFilePair("/write", 25, 7, types.WRITE_CLASSIFIED))
r.Add(newFilePair("/write", 15, 3, types.WRITE_CLASSIFIED))
r.Add(newFilePair("/xfer", 40, 5, types.TRANSFER_CLASSIFIED))
r.Add(newFilePair("/xfer", 60, 6, types.TRANSFER_CLASSIFIED))
snap := r.Snapshot()
if len(snap) != 3 {
t.Fatalf("expected 3 rows, got %d", len(snap))
}
gotOrder := paths(snap)
wantOrder := []string{"/read", "/write", "/xfer"}
if !reflect.DeepEqual(gotOrder, wantOrder) {
t.Fatalf("unexpected ranking order: got %v want %v", gotOrder, wantOrder)
}
byPath := make(map[string]FileSnapshot, len(snap))
for _, row := range snap {
byPath[row.Path] = row
}
readRow := byPath["/read"]
if readRow.Accesses != 2 || readRow.BytesRead != 20 || readRow.BytesWritten != 0 || readRow.MaxLatencyNs != 20 {
t.Fatalf("unexpected /read stats: %+v", readRow)
}
if readRow.AvgLatencyNs != 15 {
t.Fatalf("unexpected /read avg latency: %v", readRow.AvgLatencyNs)
}
writeRow := byPath["/write"]
if writeRow.BytesRead != 0 || writeRow.BytesWritten != 10 {
t.Fatalf("unexpected /write bytes split: %+v", writeRow)
}
xferRow := byPath["/xfer"]
if xferRow.BytesRead != 11 || xferRow.BytesWritten != 11 {
t.Fatalf("unexpected /xfer bytes split: %+v", xferRow)
}
if xferRow.AvgLatencyNs != 50 {
t.Fatalf("unexpected /xfer avg latency: %v", xferRow.AvgLatencyNs)
}
}
func TestFileRankerIgnoresInvalidFileData(t *testing.T) {
r := newFileRankerWithConfig(3)
r.Add(nil)
r.Add(&event.Pair{})
r.Add(&event.Pair{File: file.NewFd(1, "", -1), Duration: 10, Bytes: 1, ExitEv: &types.RetEvent{RetType: types.READ_CLASSIFIED}})
if got := r.Snapshot(); len(got) != 0 {
t.Fatalf("expected empty snapshot for invalid rows, got %+v", got)
}
}
func TestFileRankerCompactsHighCardinality(t *testing.T) {
r := newFileRankerWithLimits(3, 5)
// Make one file clearly hot so it must survive compaction.
for i := 0; i < 10; i++ {
r.Add(newFilePair("/hot", 10, 1, types.READ_CLASSIFIED))
}
for i := 0; i < 200; i++ {
r.Add(newFilePair(fmt.Sprintf("/tmp/file-%d", i), 1, 1, types.READ_CLASSIFIED))
}
if len(r.byPath) > r.maxSeen {
t.Fatalf("cardinality guard failed: byPath=%d maxSeen=%d", len(r.byPath), r.maxSeen)
}
snap := r.Snapshot()
if len(snap) == 0 || snap[0].Path != "/hot" {
t.Fatalf("expected hot file to remain top-ranked after compaction, got %+v", snap)
}
}
func newFilePair(path string, duration uint64, bytes uint64, retType uint32) *event.Pair {
return &event.Pair{
File: file.NewFd(3, path, -1),
Duration: duration,
Bytes: bytes,
ExitEv: &types.RetEvent{RetType: retType},
}
}
func paths(rows []FileSnapshot) []string {
out := make([]string, 0, len(rows))
for _, row := range rows {
out = append(out, row.Path)
}
return out
}
|