qube

package module
v1.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 33 Imported by: 0

README

qube

build Go Report Card

qube is a DB load testing tool.

Installation

brew install winebarrel/qube/qube

Usage

Usage: qube --data-files=DATA-FILES,... --dsn=DSN [flags]

Flags:
  -h, --help                         Show help.
      --[no-]force                   Do not abort test on error. (default: disabled)
  -f, --data-files=DATA-FILES,...    JSON Lines file list of queries to execute.
      --key="q"                      Key name of the query field in the test data. e.g. {"q":"SELECT ..."}
      --[no-]loop                    Return to the beginning after reading the test data. (default: enabled)
      --[no-]random                  Randomize the starting position of the test data. (default: disabled)
      --commit-rate=UINT             Number of queries to execute "COMMIT".
  -d, --dsn=DSN                      DSN to connect to. (${...} is replaced by environment variables)
                                       - MySQL: https://pkg.go.dev/github.com/go-sql-driver/mysql#readme-dsn-data-source-name
                                       - PostgreSQL: https://pkg.go.dev/github.com/jackc/pgx/v5/stdlib#pkg-overview
      --[no-]noop                    No-op mode. No actual query execution. (default: disabled)
      --[no-]iam-auth                Use RDS IAM authentication.
  -n, --nagents=1                    Number of agents.
  -r, --rate=FLOAT-64                Rate limit (qps). "0" means unlimited.
  -t, --time=DURATION                Maximum execution time of the test. "0" means unlimited.
      --[no-]progress                Show progress report.
  -C, --[no-]color                   Color report JSON.
      --version
$ echo '{"q":"select 1"}' > data.jsonl
$ echo '{"q":"select 2"}' >> data.jsonl
$ echo '{"q":"select 3"}' >> data.jsonl

$ qube -d 'root@tcp(127.0.0.1:13306)/' -f data.jsonl -n 5 -t 10s
00:05 | cpu 48% | 5 agents / exec 95788 queries, 0 errors (23637 qps)
...
{
  "ID": "b1e23c00-1601-46eb-ad2b-fdf01154243d",
  "StartedAt": "2023-11-12T12:08:29.296154+09:00",
  "FinishedAt": "2023-11-12T12:08:39.297268+09:00",
  "ElapsedTime": "10.001173875s",
  "Options": {
    "Force": false,
    "DataFile": [
      "data.jsonl"
    ],
    "Key": "q",
    "Loop": true,
    "Random": false,
    "CommitRate": 0,
    "DSN": "root@tcp(127.0.0.1:13306)/",
    "Driver": "mysql",
    "Noop": false,
    "IAMAuth": false,
    "Nagents": 5,
    "Rate": 0,
    "Time": "10s"
  },
  "GOMAXPROCS": 10,
  "QueryCount": 238001,
  "ErrorQueryCount": 0,
  "AvgQPS": 23797,
  "MaxQPS": 24977,
  "MinQPS": 21623,
  "MedianQPS": 24051.5,
  "Duration": {
    "Time": {
      "Cumulative": "49.569869935s",
      "HMean": "200.366µs",
      "Avg": "208.275µs",
      "P50": "199.75µs",
      "P75": "222.042µs",
      "P95": "288.875µs",
      "P99": "363.375µs",
      "P999": "594.208µs",
      "Long5p": "349.679µs",
      "Short5p": "142.483µs",
      "Max": "2.796209ms",
      "Min": "98.709µs",
      "Range": "2.6975ms",
      "StdDev": "54.681µs"
    },
    "Rate": {
      "Second": 4801.323875008872
    },
    "Samples": 238001,
    "Count": 238001,
    "Histogram": [
      {
        "98µs - 368µs": 235807
      },
      {
        "368µs - 638µs": 2008
      },
      {
        "638µs - 907µs": 117
      },
      {
        "907µs - 1.177ms": 19
      },
      {
        "1.177ms - 1.447ms": 9
      },
      {
        "1.447ms - 1.717ms": 4
      },
      {
        "1.717ms - 1.986ms": 2
      },
      {
        "1.986ms - 2.256ms": 3
      },
      {
        "2.256ms - 2.526ms": 12
      },
      {
        "2.526ms - 2.796ms": 20
      }
    ]
  },
  "Version": "1.7.2"
}
Comment out in the data file

Lines starting with // are ignored as comments.

{"q":"select 1"}
//comment
//{"q":"select 2"}
{"q":"select 3"}
Use Environment Variables in DSN
$ export PASSWORD=mypass
$ qube -d 'root:${PASSWORD}@tcp(127.0.0.1:13306)/' -f data.jsonl -n 5 -t 10s
00:05 | cpu 47% | 5 agents / exec 95788 queries, 0 errors (23637 qps)
...
{
  "Options": {
    "DSN": "root:${PASSWORD}@tcp(127.0.0.1:13306)/",
Use Zstandard Seekable Format for data file
$ go install github.com/SaveTheRbtz/zstd-seekable-format-go/cmd/zstdseek@latest
$ zstdseek -f data.jsonl -o data.jsonl.zst
$ ls -lh salaries.jsonl*
-rw-r--r--@ 1 sugawara  staff   235M  7 13 09:28 salaries.jsonl
-rw-r--r--@ 1 sugawara  staff    23M  7 13 09:36 salaries.jsonl.zst
$ qube -d 'root@tcp(127.0.0.1:13306)/employees' -f salaries.jsonl.zst -n 5 -t 10s --random
00:05 | cpu 50% | 5 agents / exec 89821 queries, 0 errors (22555 qps)
...
{
  "Options": {
    "DataFiles": [
      "salaries.jsonl.zst"

see https://github.com/SaveTheRbtz/zstd-seekable-format-go

Test

docker compose up -d
make testacc

Tools

Documentation

Index

Constants

View Source
const (
	InterimReportIntvl = 1 * time.Second
)
View Source
const (
	RecIntvl = 1 * time.Second
)

Variables

View Source
var (
	// End of data
	ErrEOD = errors.New("EOD")
)

Functions

This section is empty.

Types

type Agent

type Agent struct {
	*AgentOptions
	ID string
	// contains filtered or unexported fields
}

func NewAgent

func NewAgent(taskID string, agentNum uint64, options *Options, rec *Recorder, limiter *rate.Limiter) (*Agent, error)

func (*Agent) Start

func (agent *Agent) Start(ctx context.Context) error

type AgentOptions

type AgentOptions struct {
	Force bool `kong:"negatable,default='false',help='Do not abort test on error. (default: disabled)'"`
}

type DBConfig

type DBConfig struct {
	DSN       DSN       `` /* 266-byte string literal not displayed */
	Driver    DBDriver  `kong:"-"`
	Noop      bool      `kong:"negatable,default='false',help='No-op mode. No actual query execution. (default: disabled)'"`
	IAMAuth   bool      `kong:"negatable,default='false',help='Use RDS IAM authentication.'"`
	NullDBOut io.Writer `json:"-" kong:"-"`
}

func (*DBConfig) OpenDBWithPing added in v0.3.0

func (config *DBConfig) OpenDBWithPing(autoCommit bool) (DBIface, error)

type DBDriver added in v0.2.0

type DBDriver string
const (
	DBDriverMySQL      DBDriver = "mysql"
	DBDriverPostgreSQL DBDriver = "pgx"
)

func (DBDriver) String added in v1.5.0

func (driver DBDriver) String() string

type DBIface

type DBIface interface {
	Exec(query string, args ...any) (sql.Result, error)
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	Close() error
}

type DSN added in v1.5.0

type DSN string

func (DSN) Fill added in v1.5.0

func (dsn DSN) Fill() string

func (DSN) String added in v1.5.0

func (dsn DSN) String() string

type Data

type Data struct {
	*DataOptions
	// contains filtered or unexported fields
}

func NewData

func NewData(options *Options, agentNum uint64) (*Data, error)

func (*Data) Close

func (data *Data) Close() error

func (*Data) Next

func (data *Data) Next() (string, error)

type DataOptions

type DataOptions struct {
	DataFiles  []string `kong:"short='f',required,help='JSON Lines file list of queries to execute.'"`
	Key        string   `kong:"default='q',help='Key name of the query field in the test data. e.g. {\"q\":\"SELECT ...\"}'"`
	Loop       bool     `kong:"negatable,default='true',help='Return to the beginning after reading the test data. (default: enabled)'"`
	Random     bool     `kong:"negatable,default='false',help='Randomize the starting position of the test data. (default: disabled)'"`
	CommitRate uint     `kong:"help='Number of queries to execute \"COMMIT\".'"`
}

type DataPoint

type DataPoint struct {
	Time     int64
	Duration time.Duration
}

type DataPointWithErr added in v1.0.0

type DataPointWithErr struct {
	DataPoint
	IsError bool
}

type JSONDuration

type JSONDuration time.Duration

func (JSONDuration) MarshalJSON

func (jd JSONDuration) MarshalJSON() (b []byte, err error)

type NullDB

type NullDB struct {
	// contains filtered or unexported fields
}

func (*NullDB) Close

func (db *NullDB) Close() error

func (*NullDB) Exec

func (db *NullDB) Exec(query string, args ...any) (sql.Result, error)

func (*NullDB) ExecContext

func (db *NullDB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

type Options

type Options struct {
	AgentOptions
	DataOptions
	DBConfig
	Nagents  uint64        `kong:"short='n',default='1',help='Number of agents.'"`
	Rate     float64       `kong:"short='r',help='Rate limit (qps). \"0\" means unlimited.'"`
	Time     time.Duration `json:"-" kong:"short='t',help='Maximum execution time of the test. \"0\" means unlimited.'"`
	X_Time   JSONDuration  `json:"Time" kong:"-"` // for report
	Progress bool          `json:"-" kong:"negatable,help='Show progress report.'"`
	Color    bool          `json:"-" kong:"negatable,short='C',help='Color report JSON.'"`
}

func (*Options) AfterApply

func (options *Options) AfterApply() error

type Progress

type Progress struct {
	// contains filtered or unexported fields
}

func NewProgress

func NewProgress(w TTY, noop bool) *Progress

func (*Progress) Close added in v0.1.1

func (progress *Progress) Close()

func (*Progress) IncrDead

func (progress *Progress) IncrDead()

func (*Progress) Start

func (progress *Progress) Start(ctx context.Context, rec *Recorder)

type QPSSet

type QPSSet []float64

func NewQPSSet

func NewQPSSet(dps []DataPoint) QPSSet

func (QPSSet) Stats

func (qpsSet QPSSet) Stats() (minQPS float64, maxQPS float64, medianQPS float64)

type Recorder

type Recorder struct {
	*Options
	ID string

	StartedAt  time.Time
	FinishedAt time.Time
	// contains filtered or unexported fields
}

func NewRecorder

func NewRecorder(id string, options *Options) *Recorder

func (*Recorder) Add

func (rec *Recorder) Add(dpes []DataPointWithErr)

func (*Recorder) Close

func (rec *Recorder) Close()

func (*Recorder) CountAll added in v1.0.0

func (rec *Recorder) CountAll() int

func (*Recorder) CountSuccess added in v1.0.0

func (rec *Recorder) CountSuccess() int

func (*Recorder) DataPoints

func (rec *Recorder) DataPoints() []DataPoint

func (*Recorder) ErrorQueryCount

func (rec *Recorder) ErrorQueryCount() int

func (*Recorder) Report

func (rec *Recorder) Report() *Report

func (*Recorder) Start

func (rec *Recorder) Start()

type Report

type Report struct {
	ID              string
	StartedAt       time.Time
	FinishedAt      time.Time
	ElapsedTime     JSONDuration
	Options         *Options
	GOMAXPROCS      int
	QueryCount      int
	ErrorQueryCount int
	AvgQPS          float64
	MaxQPS          float64
	MinQPS          float64
	MedianQPS       float64
	Duration        *tachymeter.Metrics
	Version         string `json:",omitempty"`
}

func NewReport

func NewReport(rec *Recorder) *Report

func (*Report) JSON added in v1.0.0

func (report *Report) JSON() string

type TTY added in v1.0.3

type TTY interface {
	io.Writer
	Fd() uintptr
}

type Task

type Task struct {
	*Options
	ID string
}

func NewTask

func NewTask(options *Options) *Task

func (*Task) Run

func (task *Task) Run() (*Report, error)

type ZstdFile added in v1.6.0

type ZstdFile struct {
	// contains filtered or unexported fields
}

func NewZstdFile added in v1.6.0

func NewZstdFile(file *os.File) (*ZstdFile, error)

func (*ZstdFile) Close added in v1.6.0

func (zf *ZstdFile) Close() error

func (*ZstdFile) Read added in v1.6.0

func (zf *ZstdFile) Read(p []byte) (n int, err error)

func (*ZstdFile) Seek added in v1.6.0

func (zf *ZstdFile) Seek(offset int64, whence int) (int64, error)

func (*ZstdFile) Size added in v1.6.0

func (zf *ZstdFile) Size() int64

Directories

Path Synopsis
cmd
qube command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL