-
Notifications
You must be signed in to change notification settings - Fork 402
/
main.go
357 lines (307 loc) · 9.17 KB
/
main.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"strconv"
"text/tabwriter"
"time"
"github.com/loov/hrtime"
"storj.io/storj/internal/memory"
"storj.io/storj/internal/s3client"
)
func main() {
var conf s3client.Config
flag.StringVar(&conf.S3Gateway, "s3-gateway", "127.0.0.1:7777", "s3 gateway address")
flag.StringVar(&conf.Satellite, "satellite", "127.0.0.1:7778", "satellite address")
flag.StringVar(&conf.AccessKey, "accesskey", "insecure-dev-access-key", "access key")
flag.StringVar(&conf.SecretKey, "secretkey", "insecure-dev-secret-key", "secret key")
flag.StringVar(&conf.APIKey, "apikey", "abc123", "api key")
flag.StringVar(&conf.EncryptionKey, "encryptionkey", "abc123", "encryption key")
flag.BoolVar(&conf.NoSSL, "no-ssl", false, "disable ssl")
clientName := flag.String("client", "minio", "client to use for requests (supported: minio, aws-cli, uplink)")
location := flag.String("location", "", "bucket location")
count := flag.Int("count", 50, "benchmark count")
duration := flag.Duration("time", 2*time.Minute, "maximum benchmark time per filesize")
suffix := time.Now().Format("-2006-01-02-150405")
plotname := flag.String("plot", "plot"+suffix+".svg", "plot results")
filesizes := &memory.Sizes{
Default: []memory.Size{
1 * memory.KiB,
256 * memory.KiB,
1 * memory.MiB,
32 * memory.MiB,
64 * memory.MiB,
256 * memory.MiB,
},
}
flag.Var(filesizes, "filesize", "filesizes to test with")
listsize := flag.Int("listsize", 1000, "listsize to test with")
flag.Parse()
var client s3client.Client
var err error
switch *clientName {
default:
log.Println("unknown client name ", *clientName, " defaulting to minio")
fallthrough
case "minio":
client, err = s3client.NewMinio(conf)
case "aws-cli":
client, err = s3client.NewAWSCLI(conf)
case "uplink":
client, err = s3client.NewUplink(conf)
}
if err != nil {
log.Fatal(err)
}
bucket := "benchmark" + suffix
log.Println("Creating bucket", bucket)
// 1 bucket for file up and downloads
err = client.MakeBucket(bucket, *location)
if err != nil {
log.Fatalf("failed to create bucket %q: %+v\n", bucket, err)
}
data := make([]byte, 1)
log.Println("Creating files", bucket)
// n files in one folder
for k := 0; k < *listsize; k++ {
err := client.Upload(bucket, "folder/data"+strconv.Itoa(k), data)
if err != nil {
log.Fatalf("failed to create file %q: %+v\n", "folder/data"+strconv.Itoa(k), err)
}
}
log.Println("Creating folders", bucket)
// n - 1 (one folder already exists) folders with one file in each folder
for k := 0; k < *listsize-1; k++ {
err := client.Upload(bucket, "folder"+strconv.Itoa(k)+"/data", data)
if err != nil {
log.Fatalf("failed to create folder %q: %+v\n", "folder"+strconv.Itoa(k)+"/data", err)
}
}
defer func() {
log.Println("Removing files")
for k := 0; k < *listsize; k++ {
err := client.Delete(bucket, "folder/data"+strconv.Itoa(k))
if err != nil {
log.Fatalf("failed to delete file %q: %+v\n", "folder/data"+strconv.Itoa(k), err)
}
}
log.Println("Removing folders")
for k := 0; k < *listsize-1; k++ {
err := client.Delete(bucket, "folder"+strconv.Itoa(k)+"/data")
if err != nil {
log.Fatalf("failed to delete folder %q: %+v\n", "folder"+strconv.Itoa(k)+"/data", err)
}
}
log.Println("Removing bucket")
err := client.RemoveBucket(bucket)
if err != nil {
log.Fatalf("failed to remove bucket %q", bucket)
}
}()
measurements := []Measurement{}
measurement, err := ListBenchmark(client, bucket, *listsize, *count, *duration)
if err != nil {
log.Fatal(err)
}
measurements = append(measurements, measurement)
for _, filesize := range filesizes.Sizes() {
measurement, err := FileBenchmark(client, bucket, filesize, *count, *duration)
if err != nil {
log.Fatal(err)
}
measurements = append(measurements, measurement)
}
fmt.Print("\n\n")
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
"Size", "",
"Avg", "",
"Max", "",
"P50", "", "P90", "", "P99", "",
)
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
"", "",
"s", "MB/s",
"s", "MB/s",
"s", "MB/s", "s", "MB/s", "s", "MB/s",
)
for _, m := range measurements {
m.PrintStats(w)
}
_ = w.Flush()
if *plotname != "" {
err := Plot(*plotname, measurements)
if err != nil {
log.Fatal(err)
}
}
}
// Measurement contains measurements for different requests
type Measurement struct {
Size memory.Size
Results []*Result
}
// Result contains durations for specific tests
type Result struct {
Name string
WithSpeed bool
Durations []time.Duration
}
// Result finds or creates a result with the specified name
func (m *Measurement) Result(name string) *Result {
for _, x := range m.Results {
if x.Name == name {
return x
}
}
r := &Result{}
r.Name = name
m.Results = append(m.Results, r)
return r
}
// Record records a time measurement
func (m *Measurement) Record(name string, duration time.Duration) {
r := m.Result(name)
r.WithSpeed = false
r.Durations = append(r.Durations, duration)
}
// RecordSpeed records a time measurement that can be expressed in speed
func (m *Measurement) RecordSpeed(name string, duration time.Duration) {
r := m.Result(name)
r.WithSpeed = true
r.Durations = append(r.Durations, duration)
}
// PrintStats prints important valueas about the measurement
func (m *Measurement) PrintStats(w io.Writer) {
const binCount = 10
type Hist struct {
*Result
*hrtime.Histogram
}
hists := []Hist{}
for _, result := range m.Results {
hists = append(hists, Hist{
Result: result,
Histogram: hrtime.NewDurationHistogram(result.Durations, binCount),
})
}
sec := func(ns float64) string {
return fmt.Sprintf("%.2f", ns/1e9)
}
speed := func(ns float64) string {
return fmt.Sprintf("%.2f", m.Size.MB()/(ns/1e9))
}
for _, hist := range hists {
if !hist.WithSpeed {
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
m.Size, hist.Name,
sec(hist.Average), "",
sec(hist.Maximum), "",
sec(hist.P50), "",
sec(hist.P90), "",
sec(hist.P99), "",
)
continue
}
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
m.Size, hist.Name,
sec(hist.Average), speed(hist.Average),
sec(hist.Maximum), speed(hist.Maximum),
sec(hist.P50), speed(hist.P50),
sec(hist.P90), speed(hist.P90),
sec(hist.P99), speed(hist.P99),
)
}
}
// FileBenchmark runs file upload, download and delete benchmarks on bucket with given filesize
func FileBenchmark(client s3client.Client, bucket string, filesize memory.Size, count int, duration time.Duration) (Measurement, error) {
log.Print("Benchmarking file size ", filesize.String(), " ")
data := make([]byte, filesize.Int())
result := make([]byte, filesize.Int())
defer fmt.Println()
measurement := Measurement{}
measurement.Size = filesize
start := time.Now()
for k := 0; k < count; k++ {
if time.Since(start) > duration {
break
}
fmt.Print(".")
// rand.Read(data[:])
for i := range data {
data[i] = 'a' + byte(i%26)
}
{ // uploading
start := hrtime.Now()
err := client.Upload(bucket, "data", data)
finish := hrtime.Now()
if err != nil {
return measurement, fmt.Errorf("upload failed: %+v", err)
}
measurement.RecordSpeed("Upload", finish-start)
}
{ // downloading
start := hrtime.Now()
var err error
result, err = client.Download(bucket, "data", result)
if err != nil {
return measurement, fmt.Errorf("get object failed: %+v", err)
}
finish := hrtime.Now()
if !bytes.Equal(data, result) {
return measurement, fmt.Errorf("upload/download do not match: lengths %d and %d", len(data), len(result))
}
measurement.RecordSpeed("Download", finish-start)
}
{ // deleting
start := hrtime.Now()
err := client.Delete(bucket, "data")
if err != nil {
return measurement, fmt.Errorf("delete failed: %+v", err)
}
finish := hrtime.Now()
measurement.Record("Delete", finish-start)
}
}
return measurement, nil
}
// ListBenchmark runs list buckets, folders and files benchmarks on bucket
func ListBenchmark(client s3client.Client, bucket string, listsize int, count int, duration time.Duration) (Measurement, error) {
log.Print("Benchmarking list")
defer fmt.Println()
measurement := Measurement{}
//measurement.Size = listsize
for k := 0; k < count; k++ {
{ // list folders
start := hrtime.Now()
result, err := client.ListObjects(bucket, "")
if err != nil {
return measurement, fmt.Errorf("list folders failed: %+v", err)
}
finish := hrtime.Now()
if len(result) != listsize {
return measurement, fmt.Errorf("list folders result wrong: %+v", len(result))
}
measurement.Record("List Folders", finish-start)
}
{ // list files
start := hrtime.Now()
result, err := client.ListObjects(bucket, "folder")
if err != nil {
return measurement, fmt.Errorf("list files failed: %+v", err)
}
finish := hrtime.Now()
if len(result) != listsize {
return measurement, fmt.Errorf("list files result to low: %+v", len(result))
}
measurement.Record("List Files", finish-start)
}
}
return measurement, nil
}