Skip to content
This repository was archived by the owner on Jan 25, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/412fa22ba5f8452794584ed9819f149b)](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=Seagate/s3bench&utm_campaign=Badge_Grade)

# Initial
Cloned from
```
https://github.com/igneous-systems/s3bench.git
```

# S3 Bench
This tool offers the ability to run very basic throughput benchmarking against
an S3-compatible endpoint. It does a series of put operations followed by a
Expand Down Expand Up @@ -125,3 +133,10 @@ Put times 50th %ile: 0.001 s
Put times 25th %ile: 0.001 s
Put times Min: 0.001 s
```

##### Head-object
It is possible to send head-object requests instead of get-object.
For this purpose one sould use *-metaData* flag
```
./s3bench -accessKey=KEY -accessSecret=SECRET -bucket=loadgen -endpoint=http://endpoint1:80 -numClients=2 -numSamples=10 -objectNamePrefix=loadgen -objectSize=1024 -metaData
```
12 changes: 12 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -e

now=$(date +'%Y-%m-%d-%T')
githash=$(git rev-parse HEAD)

echo "Building version $now-$githash..."

go build -ldflags "-X main.gitHash=$githash -X main.buildDate=$now"

echo "Complete"
67 changes: 67 additions & 0 deletions data_decl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import "time"

var (
gitHash string
buildDate string
)

const (
opRead = "Read"
opWrite = "Write"
opHeadObj = "HeadObj"
opGetObjTag = "GetObjTag"
opPutObjTag = "PutObjTag"
opValidate = "Validate"
)

type Req struct {
top string
req interface{}
}

type Resp struct {
err error
duration time.Duration
numBytes int64
ttfb time.Duration
}

// Specifies the parameters for a given test
type Params struct {
requests chan Req
responses chan Resp
numSamples uint
numClients uint
objectSize int64
objectNamePrefix string
bucketName string
endpoints []string
verbose bool
headObj bool
sampleReads uint
clientDelay int
jsonOutput bool
deleteAtOnce int
putObjTag bool
getObjTag bool
numTags uint
readObj bool
tagNamePrefix string
tagValPrefix string
reportFormat string
validate bool
skipWrite bool
skipRead bool
}

// Contains the summary for a given test result
type Result struct {
operation string
bytesTransmitted int64
opDurations []float64
totalDuration time.Duration
opTtfb []float64
opErrors []string
}
177 changes: 177 additions & 0 deletions report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"fmt"
"sort"
"strings"
"encoding/json"
)

func keysSort(keys []string, format []string) []string {
sort.Strings(keys)
cur_formated := 0

for _, fv := range format {
fv = strings.TrimSpace(fv)
should_del := strings.HasPrefix(fv, "-")
if should_del {
fv = fv[1:]
}
ci := indexOf(keys, fv)
if ci < 0 {
continue
}
// delete old pos
keys = append(keys[:ci], keys[ci+1:]...)

if !should_del {
// insert new pos
keys = append(keys[:cur_formated], append([]string{fv}, keys[cur_formated:]...)...)
cur_formated++
}
}

return keys
}

func formatFilter(format []string, key string) []string {
ret := []string{}
for _, v := range format {
if strings.HasPrefix(v, key + ":") {
ret = append(ret, v[len(key + ":"):])
} else if strings.HasPrefix(v, "-" + key + ":") {
ret = append(ret, "-" + v[len("-" + key + ":"):])
}
}

return ret
}

func mapPrint(m map[string]interface{}, repFormat []string, prefix string) {
var mkeys []string
for k,_ := range m {
mkeys = append(mkeys, k)
}
mkeys = keysSort(mkeys, repFormat)
for _, k := range mkeys {
v := m[k]
fmt.Printf("%s %-27s", prefix, k+":")
switch val := v.(type) {
case []string:
if len(val) == 0 {
fmt.Printf(" []\n")
} else {
fmt.Println()
for _, s := range val {
fmt.Printf("%s%s %s\n", prefix, prefix, s)
}
}
case map[string]interface{}:
fmt.Println()
mapPrint(val, formatFilter(repFormat, k), prefix + " ")
case []map[string]interface{}:
if len(val) == 0 {
fmt.Printf(" []\n")
} else {
val_format := formatFilter(repFormat, k)
for _, m := range val {
fmt.Println()
mapPrint(m, val_format, prefix + " ")
}
}
case float64:
fmt.Printf(" %.3f\n", val)
default:
fmt.Printf(" %v\n", val)
}
}
}

func (params Params) reportPrepare(tests []Result) map[string]interface{} {
report := make(map[string]interface{})
report["Version"] = fmt.Sprintf("%s-%s", buildDate, gitHash)
report["Parameters"] = params.report()
testreps := make([]map[string]interface{}, 0, len(tests))
for _, r := range tests {
testreps = append(testreps, r.report())
}
report["Tests"] = testreps
return report
}

func (params Params) reportPrint(report map[string]interface{}) {
if params.jsonOutput {
b, err := json.Marshal(report)
if err != nil {
fmt.Println("Cannot generate JSON report %v", err)
}
fmt.Println(string(b))
return
}

mapPrint(report, strings.Split(params.reportFormat, ";"), "")
}

func (r Result) report() map[string]interface{} {
ret := make(map[string]interface{})
ret["Operation"] = r.operation
ret["Total Requests Count"] = len(r.opDurations)
if r.operation == opWrite || r.operation == opRead || r.operation == opValidate {
ret["Total Transferred (MB)"] = float64(r.bytesTransmitted)/(1024*1024)
ret["Total Throughput (MB/s)"] = (float64(r.bytesTransmitted)/(1024*1024))/r.totalDuration.Seconds()
}
ret["Total Duration (s)"] = r.totalDuration.Seconds()

if len(r.opDurations) > 0 {
ret["Duration Max"] = percentile(r.opDurations, 100)
ret["Duration Avg"] = avg(r.opDurations)
ret["Duration Min"] = percentile(r.opDurations, 0)
ret["Duration 99th-ile"] = percentile(r.opDurations, 99)
ret["Duration 90th-ile"] = percentile(r.opDurations, 90)
ret["Duration 75th-ile"] = percentile(r.opDurations, 75)
ret["Duration 50th-ile"] = percentile(r.opDurations, 50)
ret["Duration 25th-ile"] = percentile(r.opDurations, 25)
}

if len(r.opTtfb) > 0 {
ret["Ttfb Max"] = percentile(r.opTtfb, 100)
ret["Ttfb Avg"] = avg(r.opTtfb)
ret["Ttfb Min"] = percentile(r.opTtfb, 0)
ret["Ttfb 99th-ile"] = percentile(r.opTtfb, 99)
ret["Ttfb 90th-ile"] = percentile(r.opTtfb, 90)
ret["Ttfb 75th-ile"] = percentile(r.opTtfb, 75)
ret["Ttfb 50th-ile"] = percentile(r.opTtfb, 50)
ret["Ttfb 25th-ile"] = percentile(r.opTtfb, 25)
}

ret["Errors Count"] = len(r.opErrors)
ret["Errors"] = r.opErrors
return ret
}

func (params Params) report() map[string]interface{} {
ret := make(map[string]interface{})
ret["endpoints"] = params.endpoints
ret["bucket"] = params.bucketName
ret["objectNamePrefix"] = params.objectNamePrefix
ret["objectSize (MB)"] = float64(params.objectSize)/(1024*1024)
ret["numClients"] = params.numClients
ret["numSamples"] = params.numSamples
ret["sampleReads"] = params.sampleReads
ret["verbose"] = params.verbose
ret["headObj"] = params.headObj
ret["clientDelay"] = params.clientDelay
ret["jsonOutput"] = params.jsonOutput
ret["deleteAtOnce"] = params.deleteAtOnce
ret["numTags"] = params.numTags
ret["putObjTag"] = params.putObjTag
ret["getObjTag"] = params.getObjTag
ret["readObj"] = params.readObj
ret["tagNamePrefix"] = params.tagNamePrefix
ret["tagValPrefix"] = params.tagValPrefix
ret["reportFormat"] = params.reportFormat
ret["validate"] = params.validate
ret["skipWrite"] = params.skipWrite
ret["skipRead"] = params.skipRead
return ret
}
Loading