package ctl import ( "context" "errors" "flag" "fmt" "io" "io/ioutil" "math/rand" "strings" "time" "github.com/pilosa/pilosa" ) // BenchCommand represents a command for benchmarking database operations. type BenchCommand struct { // Destination host and port. Host string // Name of the database & frame to execute against. Database string Frame string // Type of operation and number to execute. Op string N int // Standard input/output Stdin io.Reader Stdout io.Writer Stderr io.Writer } // NewBenchCommand returns a new instance of BenchCommand. func NewBenchCommand(stdin io.Reader, stdout, stderr io.Writer) *BenchCommand { return &BenchCommand{ Stdin: stdin, Stdout: stdout, Stderr: stderr, } } // ParseFlags parses command line flags from args. func (cmd *BenchCommand) ParseFlags(args []string) error { fs := flag.NewFlagSet("pilosactl", flag.ContinueOnError) fs.SetOutput(ioutil.Discard) fs.StringVar(&cmd.Host, "host", "localhost:15000", "host:port") fs.StringVar(&cmd.Database, "d", "", "database") fs.StringVar(&cmd.Frame, "f", "", "frame") fs.StringVar(&cmd.Op, "op", "", "operation") fs.IntVar(&cmd.N, "n", 0, "op count") if err := fs.Parse(args); err != nil { return err } return nil } // Usage returns the usage message to be printed. func (cmd *BenchCommand) Usage() string { return strings.TrimSpace(` usage: pilosactl bench [args] Executes a benchmark for a given operation against the database. The following flags are allowed: -host HOSTPORT hostname and port of running pilosa server -d DATABASE database to execute operation against -f FRAME frame to execute operation against -op OP name of operation to execute -n COUNT number of iterations to execute The following operations are available: set-bit Sets a single random bit on the frame `) } // Run executes the main program execution. func (cmd *BenchCommand) Run(ctx context.Context) error { // Create a client to the server. client, err := pilosa.NewClient(cmd.Host) if err != nil { return err } switch cmd.Op { case "set-bit": return cmd.runSetBit(ctx, client) case "": return errors.New("op required") default: return fmt.Errorf("unknown bench op: %q", cmd.Op) } } // runSetBit executes a benchmark of random SetBit() operations. func (cmd *BenchCommand) runSetBit(ctx context.Context, client *pilosa.Client) error { if cmd.N == 0 { return errors.New("operation count required") } else if cmd.Database == "" { return pilosa.ErrDatabaseRequired } else if cmd.Frame == "" { return pilosa.ErrFrameRequired } const maxBitmapID = 1000 const maxProfileID = 100000 startTime := time.Now() // Execute operation continuously. for i := 0; i < cmd.N; i++ { bitmapID := rand.Intn(maxBitmapID) profileID := rand.Intn(maxProfileID) q := fmt.Sprintf(`SetBit(id=%d, frame="%s", profileID=%d)`, bitmapID, cmd.Frame, profileID) if _, err := client.ExecuteQuery(ctx, cmd.Database, q, true); err != nil { return err } } // Print results. elapsed := time.Since(startTime) fmt.Fprintf(cmd.Stdout, "Executed %d operations in %s (%0.3f op/sec)\n", cmd.N, elapsed, float64(cmd.N)/elapsed.Seconds()) return nil }