|
1 | 1 | package collect |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "encoding/json" |
5 | | - "log" |
6 | 4 | "math" |
7 | 5 | "math/rand" |
8 | | - "os" |
9 | | - "path/filepath" |
10 | | - "sort" |
11 | | - "sync" |
12 | | - "syscall" |
13 | 6 | "time" |
14 | | - |
15 | | - "github.com/pkg/errors" |
16 | | - "k8s.io/apimachinery/pkg/api/resource" |
17 | 7 | ) |
18 | 8 |
|
19 | 9 | func init() { |
@@ -44,209 +34,6 @@ type FSPerfResults struct { |
44 | 34 | IOPS int |
45 | 35 | } |
46 | 36 |
|
47 | | -type Durations []time.Duration |
48 | | - |
49 | | -func (d Durations) Len() int { |
50 | | - return len(d) |
51 | | -} |
52 | | - |
53 | | -func (d Durations) Less(i, j int) bool { |
54 | | - return d[i] < d[j] |
55 | | -} |
56 | | - |
57 | | -func (d Durations) Swap(i, j int) { |
58 | | - d[i], d[j] = d[j], d[i] |
59 | | -} |
60 | | - |
61 | | -func HostFilesystemPerformance(c *HostCollector) (map[string][]byte, error) { |
62 | | - var operationSize uint64 = 1024 |
63 | | - if c.Collect.FilesystemPerformance.OperationSizeBytes != 0 { |
64 | | - operationSize = c.Collect.FilesystemPerformance.OperationSizeBytes |
65 | | - } |
66 | | - |
67 | | - var fileSize uint64 = 10 * 1024 * 1024 |
68 | | - if c.Collect.FilesystemPerformance.FileSize != "" { |
69 | | - quantity, err := resource.ParseQuantity(c.Collect.FilesystemPerformance.FileSize) |
70 | | - if err != nil { |
71 | | - return nil, errors.Wrapf(err, "failed to parse fileSize %q", c.Collect.FilesystemPerformance.FileSize) |
72 | | - } |
73 | | - fileSizeInt64, ok := quantity.AsInt64() |
74 | | - if !ok { |
75 | | - return nil, errors.Wrapf(err, "failed to parse fileSize %q", c.Collect.FilesystemPerformance.FileSize) |
76 | | - } |
77 | | - fileSize = uint64(fileSizeInt64) |
78 | | - } |
79 | | - |
80 | | - if c.Collect.FilesystemPerformance.Directory == "" { |
81 | | - return nil, errors.New("Directory is required to collect filesystem performance info") |
82 | | - } |
83 | | - if err := os.MkdirAll(c.Collect.FilesystemPerformance.Directory, 0700); err != nil { |
84 | | - return nil, errors.Wrapf(err, "failed to mkdir %q", c.Collect.FilesystemPerformance.Directory) |
85 | | - } |
86 | | - filename := filepath.Join(c.Collect.FilesystemPerformance.Directory, "fsperf") |
87 | | - |
88 | | - f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) |
89 | | - if err != nil { |
90 | | - return nil, errors.Wrapf(err, "open %s", filename) |
91 | | - } |
92 | | - defer func() { |
93 | | - if err := f.Close(); err != nil { |
94 | | - log.Println(err.Error()) |
95 | | - } |
96 | | - if err := os.Remove(filename); err != nil { |
97 | | - log.Println(err.Error()) |
98 | | - } |
99 | | - }() |
100 | | - |
101 | | - // Sequential writes benchmark |
102 | | - var written uint64 = 0 |
103 | | - var results Durations |
104 | | - |
105 | | - for { |
106 | | - if written >= fileSize { |
107 | | - break |
108 | | - } |
109 | | - |
110 | | - data := make([]byte, int(operationSize)) |
111 | | - rand.Read(data) |
112 | | - |
113 | | - start := time.Now() |
114 | | - |
115 | | - n, err := f.Write(data) |
116 | | - if err != nil { |
117 | | - return nil, errors.Wrapf(err, "write to %s", filename) |
118 | | - } |
119 | | - if c.Collect.FilesystemPerformance.Sync { |
120 | | - if err := f.Sync(); err != nil { |
121 | | - return nil, errors.Wrapf(err, "sync %s", filename) |
122 | | - } |
123 | | - } else if c.Collect.FilesystemPerformance.Datasync { |
124 | | - if err := syscall.Fdatasync(int(f.Fd())); err != nil { |
125 | | - return nil, errors.Wrapf(err, "datasync %s", filename) |
126 | | - } |
127 | | - } |
128 | | - |
129 | | - d := time.Now().Sub(start) |
130 | | - results = append(results, d) |
131 | | - |
132 | | - written += uint64(n) |
133 | | - } |
134 | | - |
135 | | - if len(results) == 0 { |
136 | | - return nil, errors.New("No filesystem performance results collected") |
137 | | - } |
138 | | - |
139 | | - sort.Sort(results) |
140 | | - |
141 | | - var sum time.Duration |
142 | | - for _, d := range results { |
143 | | - sum += d |
144 | | - } |
145 | | - |
146 | | - fsPerf := &FSPerfResults{ |
147 | | - Min: results[0], |
148 | | - Max: results[len(results)-1], |
149 | | - Average: sum / time.Duration(len(results)), |
150 | | - P1: results[getPercentileIndex(.01, len(results))], |
151 | | - P5: results[getPercentileIndex(.05, len(results))], |
152 | | - P10: results[getPercentileIndex(.1, len(results))], |
153 | | - P20: results[getPercentileIndex(.2, len(results))], |
154 | | - P30: results[getPercentileIndex(.3, len(results))], |
155 | | - P40: results[getPercentileIndex(.4, len(results))], |
156 | | - P50: results[getPercentileIndex(.5, len(results))], |
157 | | - P60: results[getPercentileIndex(.6, len(results))], |
158 | | - P70: results[getPercentileIndex(.7, len(results))], |
159 | | - P80: results[getPercentileIndex(.8, len(results))], |
160 | | - P90: results[getPercentileIndex(.9, len(results))], |
161 | | - P95: results[getPercentileIndex(.95, len(results))], |
162 | | - P99: results[getPercentileIndex(.99, len(results))], |
163 | | - P995: results[getPercentileIndex(.995, len(results))], |
164 | | - P999: results[getPercentileIndex(.999, len(results))], |
165 | | - P9995: results[getPercentileIndex(.9995, len(results))], |
166 | | - P9999: results[getPercentileIndex(.9999, len(results))], |
167 | | - } |
168 | | - |
169 | | - // Random IOPS benchmark |
170 | | - |
171 | | - // Re-open the file read+write in direct mode to prevent caching |
172 | | - if err := f.Close(); err != nil { |
173 | | - return nil, errors.Wrapf(err, "close %s", filename) |
174 | | - } |
175 | | - f, err = os.OpenFile(filename, os.O_RDWR|syscall.O_DIRECT, 0600) |
176 | | - if err != nil { |
177 | | - return nil, errors.Wrapf(err, "open direct %s", filename) |
178 | | - } |
179 | | - |
180 | | - offsets := make([]int64, len(results)) |
181 | | - |
182 | | - for index, p := range rand.Perm(len(results)) { |
183 | | - offsets[index] = int64(p) * int64(operationSize) |
184 | | - } |
185 | | - |
186 | | - // Use multiple workers to keep the filesystem busy. Since operations are serialized on a single |
187 | | - // file, more than 2 does not improve IOPS. |
188 | | - workers := 2 |
189 | | - wg := sync.WaitGroup{} |
190 | | - m := sync.Mutex{} |
191 | | - |
192 | | - errs := make(chan error, workers) |
193 | | - |
194 | | - start := time.Now() |
195 | | - |
196 | | - for i := 0; i < workers; i++ { |
197 | | - wg.Add(1) |
198 | | - |
199 | | - go func(i int) { |
200 | | - defer wg.Done() |
201 | | - |
202 | | - data := make([]byte, int(operationSize)) |
203 | | - fd := int(f.Fd()) |
204 | | - |
205 | | - for idx, offset := range offsets { |
206 | | - if idx%workers != i { |
207 | | - continue |
208 | | - } |
209 | | - |
210 | | - m.Lock() |
211 | | - n, err := syscall.Pread(fd, data, offset) |
212 | | - m.Unlock() |
213 | | - |
214 | | - if err != nil { |
215 | | - errs <- errors.Wrapf(err, "failed to pread %d bytes to %s at offset %d", len(data), filename, offset) |
216 | | - } |
217 | | - if n != len(data) { |
218 | | - errs <- errors.Wrapf(err, "pread %d of %d bytes to %s at offset %d", n, len(data), filename, offset) |
219 | | - } |
220 | | - } |
221 | | - }(i) |
222 | | - } |
223 | | - |
224 | | - wg.Wait() |
225 | | - if len(errs) > 0 { |
226 | | - return nil, <-errs |
227 | | - } |
228 | | - |
229 | | - d := time.Now().Sub(start) |
230 | | - nsPerIO := d / time.Duration(len(offsets)) |
231 | | - iops := time.Second / nsPerIO |
232 | | - |
233 | | - fsPerf.IOPS = int(iops) |
234 | | - |
235 | | - collectorName := c.Collect.FilesystemPerformance.CollectorName |
236 | | - if collectorName == "" { |
237 | | - collectorName = "filesystemPerformance" |
238 | | - } |
239 | | - name := filepath.Join("filesystemPerformance", collectorName+".json") |
240 | | - b, err := json.Marshal(fsPerf) |
241 | | - if err != nil { |
242 | | - return nil, errors.Wrap(err, "failed to marshal fs perf results") |
243 | | - } |
244 | | - |
245 | | - return map[string][]byte{ |
246 | | - name: b, |
247 | | - }, nil |
248 | | -} |
249 | | - |
250 | 37 | func getPercentileIndex(p float64, items int) int { |
251 | 38 | if p >= 1 { |
252 | 39 | return items - 1 |
|
0 commit comments