16513: add an option to keep-exercise for a timed run, with a CSV output
[arvados.git] / tools / keep-exercise / keep-exercise.go
index a4684739e72ad36e33920490671b4d2c222cd799..d06b1eb181012f0696a31b07c3b2e48421238f9c 100644 (file)
@@ -1,3 +1,7 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 // Testing tool for Keep services.
 //
 // keepexercise helps measure throughput and test reliability under
@@ -18,16 +22,20 @@ import (
        "crypto/rand"
        "encoding/binary"
        "flag"
+       "fmt"
        "io"
        "io/ioutil"
        "log"
        "net/http"
+       "os"
        "time"
 
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
+       "git.arvados.org/arvados.git/sdk/go/arvadosclient"
+       "git.arvados.org/arvados.git/sdk/go/keepclient"
 )
 
+var version = "dev"
+
 // Command line config knobs
 var (
        BlockSize     = flag.Int("block-size", keepclient.BLOCKSIZE, "bytes per read/write op")
@@ -39,11 +47,21 @@ var (
        StatsInterval = flag.Duration("stats-interval", time.Second, "time interval between IO stats reports, or 0 to disable")
        ServiceURL    = flag.String("url", "", "specify scheme://host of a single keep service to exercise (instead of using all advertised services like normal clients)")
        ServiceUUID   = flag.String("uuid", "", "specify UUID of a single advertised keep service to exercise")
+       getVersion    = flag.Bool("version", false, "Print version information and exit.")
+       RunTime       = flag.Duration("run-time", 0, "time to run (e.g. 60s), or 0 to run indefinitely (default)")
 )
 
 func main() {
        flag.Parse()
 
+       // Print version information if requested
+       if *getVersion {
+               fmt.Printf("keep-exercise %s\n", version)
+               os.Exit(0)
+       }
+
+       log.Printf("keep-exercise %s started", version)
+
        arv, err := arvadosclient.MakeArvadosClient()
        if err != nil {
                log.Fatal(err)
@@ -87,22 +105,54 @@ var errorsChan = make(chan struct{})
 func countBeans(nextLocator chan string) {
        t0 := time.Now()
        var tickChan <-chan time.Time
+       var endChan <-chan time.Time
        if *StatsInterval > 0 {
                tickChan = time.NewTicker(*StatsInterval).C
        }
+       if *RunTime > 0 {
+               endChan = time.NewTicker(*RunTime).C
+       }
        var bytesIn uint64
        var bytesOut uint64
        var errors uint64
+       var maxRateIn, maxRateOut float64
        for {
                select {
                case <-tickChan:
                        elapsed := time.Since(t0)
+                       if float64(bytesIn)/elapsed.Seconds()/1048576 > maxRateIn {
+                               maxRateIn = float64(bytesIn) / elapsed.Seconds() / 1048576
+                       }
+                       if float64(bytesOut)/elapsed.Seconds()/1048576 > maxRateOut {
+                               maxRateOut = float64(bytesOut) / elapsed.Seconds() / 1048576
+                       }
                        log.Printf("%v elapsed: read %v bytes (%.1f MiB/s), wrote %v bytes (%.1f MiB/s), errors %d",
                                elapsed,
                                bytesIn, (float64(bytesIn) / elapsed.Seconds() / 1048576),
                                bytesOut, (float64(bytesOut) / elapsed.Seconds() / 1048576),
                                errors,
                        )
+               case <-endChan:
+                       elapsed := time.Since(t0)
+                       log.Println("\nSummary:")
+                       log.Println("Elapsed,Read (bytes),Avg Read Speed (MiB/s),Peak Read Speed (MiB/s),Written (bytes),Avg Write Speed (MiB/s),Peak Write Speed (MiB/s),Errors,ReadThreads,WriteThreads,VaryRequest,VaryThread,BlockSize,Replicas,StatsInterval,ServiceURL,ServiceUUID,RunTime\n")
+                       log.Printf("%v,%v,%.1f,%.1f,%v,%.1f,%.1f,%d,%d,%d,%t,%t,%d,%d,%s,%s,%s,%s",
+                               elapsed,
+                               bytesIn, (float64(bytesIn) / elapsed.Seconds() / 1048576), maxRateIn,
+                               bytesOut, (float64(bytesOut) / elapsed.Seconds() / 1048576), maxRateOut,
+                               errors,
+                               *ReadThreads,
+                               *WriteThreads,
+                               *VaryRequest,
+                               *VaryThread,
+                               *BlockSize,
+                               *Replicas,
+                               *StatsInterval,
+                               *ServiceURL,
+                               *ServiceUUID,
+                               *RunTime,
+                       )
+                       os.Exit(0)
                case i := <-bytesInChan:
                        bytesIn += i
                case o := <-bytesOutChan: