Skip to content

Commit d2a3b49

Browse files
authored
Add merge of JSON data (#458)
* Add merge of JSON data Allows merging of more recent JSON (aggregated) data. Fixes #456
1 parent ada3d7e commit d2a3b49

1 file changed

Lines changed: 73 additions & 2 deletions

File tree

cli/merge.go

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package cli
1919

2020
import (
21+
"encoding/json"
2122
"errors"
2223
"fmt"
2324
"os"
@@ -27,6 +28,7 @@ import (
2728
"github.com/minio/cli"
2829
"github.com/minio/mc/pkg/probe"
2930
"github.com/minio/pkg/v3/console"
31+
"github.com/minio/warp/pkg/aggregate"
3032
"github.com/minio/warp/pkg/bench"
3133
)
3234

@@ -48,21 +50,83 @@ var mergeCmd = cli.Command{
4850
{{.HelpName}} - {{.Usage}}
4951
5052
USAGE:
51-
{{.HelpName}} [FLAGS] benchmark-data-file1 benchmark-data-file2 ...
53+
{{.HelpName}} [FLAGS] benchmark-data-file1 benchmark-data-file2 ...
5254
-> see https://github.com/minio/warp#merging-benchmarks
5355
5456
FLAGS:
5557
{{range .VisibleFlags}}{{.}}
5658
{{end}}`,
5759
}
5860

59-
// mainAnalyze is the entry point for analyze command.
6061
func mainMerge(ctx *cli.Context) error {
6162
checkMerge(ctx)
6263
args := ctx.Args()
6364
if len(args) <= 1 {
6465
console.Fatal("Two or more benchmark data files must be supplied")
6566
}
67+
68+
rc, isJSON := openInput(args[0])
69+
rc.Close()
70+
71+
if isJSON {
72+
return mergeJSON(ctx, args)
73+
}
74+
return mergeCSV(ctx, args)
75+
}
76+
77+
func timeOverlaps(a, b aggregate.LiveAggregate) bool {
78+
return a.StartTime.Before(b.EndTime) && b.StartTime.Before(a.EndTime)
79+
}
80+
81+
func mergeJSON(ctx *cli.Context, args []string) error {
82+
var merged aggregate.Realtime
83+
for i, arg := range args {
84+
rc, isJSON := openInput(arg)
85+
if !isJSON {
86+
rc.Close()
87+
fatalIf(probe.NewError(errors.New("mixed input types")), "mixed input types (JSON and CSV)")
88+
}
89+
var rt aggregate.Realtime
90+
if err := json.NewDecoder(rc).Decode(&rt); err != nil {
91+
rc.Close()
92+
fatalIf(probe.NewError(err), "Unable to parse input")
93+
}
94+
rc.Close()
95+
if i > 0 && !timeOverlaps(merged.Total, rt.Total) {
96+
fatalIf(probe.NewError(fmt.Errorf(
97+
"file %q (%s - %s) does not overlap with merged range (%s - %s)",
98+
arg, rt.Total.StartTime.Format(time.RFC3339), rt.Total.EndTime.Format(time.RFC3339),
99+
merged.Total.StartTime.Format(time.RFC3339), merged.Total.EndTime.Format(time.RFC3339),
100+
)), "time ranges do not overlap")
101+
}
102+
merged.Merge(&rt)
103+
}
104+
merged.Final = true
105+
106+
fileName := ctx.String("benchdata")
107+
if fileName == "" {
108+
fileName = fmt.Sprintf("%s-%s-%s", appName, ctx.Command.Name, time.Now().Format("2006-01-02[150405]"))
109+
}
110+
f, err := os.Create(fileName + ".json.zst")
111+
if err != nil {
112+
console.Error("Unable to write benchmark data:", err)
113+
return nil
114+
}
115+
defer f.Close()
116+
enc, err := zstd.NewWriter(f, zstd.WithEncoderLevel(zstd.SpeedBetterCompression))
117+
fatalIf(probe.NewError(err), "Unable to compress benchmark output")
118+
defer enc.Close()
119+
120+
js := json.NewEncoder(enc)
121+
js.SetIndent("", " ")
122+
err = js.Encode(merged)
123+
fatalIf(probe.NewError(err), "Unable to write benchmark output")
124+
125+
console.Infof("Benchmark data written to %q\n", fileName+".json.zst")
126+
return nil
127+
}
128+
129+
func mergeCSV(ctx *cli.Context, args []string) error {
66130
zstdDec, _ := zstd.NewReader(nil)
67131
defer zstdDec.Close()
68132
var allOps bench.Operations
@@ -72,6 +136,13 @@ func mainMerge(ctx *cli.Context) error {
72136
log = nil
73137
}
74138
for _, arg := range args {
139+
rc, isJSON := openInput(arg)
140+
if isJSON {
141+
rc.Close()
142+
fatalIf(probe.NewError(errors.New("mixed input types")), "mixed input types (JSON and CSV)")
143+
}
144+
rc.Close()
145+
75146
f, err := os.Open(arg)
76147
fatalIf(probe.NewError(err), "Unable to open input file")
77148
defer f.Close()

0 commit comments

Comments
 (0)