mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-10-30 14:22:30 +00:00
143 lines
3.1 KiB
Go
143 lines
3.1 KiB
Go
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
|
|
}
|