Compare PRO#

The compare command runs the same workload against two databases simultaneously and compares performance. It answers “is Postgres or MySQL faster for my workload?” in one command.

Each database gets its own config file with driver-specific SQL. Both configs should have matching query names so the TUI can show side-by-side results per query.

Subcommands#

CommandDescription
compare upCreate schema on both databases
compare seedPopulate tables on both databases
compare runRun workload on both databases simultaneously
compare deseedDelete seeded data from both databases
compare downTear down schema on both databases
compare allRun up, seed, run, deseed, and down on both databases

Flags#

Connection flags#

These persistent flags apply to all compare subcommands:

Flag / Env VarDefaultDescription
--a-driver
EDG_COMPARE_A_DRIVER
pgxDatabase driver for side A (pgx, mysql, mongodb, cassandra, mssql, oracle, dsql, spanner)
--a-url
EDG_COMPARE_A_URL
Database connection URL for side A
--a-config
EDG_COMPARE_A_CONFIG
Path to the edg config file for side A
--b-driver
EDG_COMPARE_B_DRIVER
pgxDatabase driver for side B (pgx, mysql, mongodb, cassandra, mssql, oracle, dsql, spanner)
--b-url
EDG_COMPARE_B_URL
Database connection URL for side B
--b-config
EDG_COMPARE_B_CONFIG
Path to the edg config file for side B

Run flags#

These flags are specific to compare run and compare all:

FlagShortDefaultDescription
--duration-d1mBenchmark duration
--workers-w1Number of concurrent workers per database. Values greater than 1 require a pro license.
PRO
--print-interval1sProgress reporting interval
--warmup-duration0Warmup period before collecting metrics. Workers run during warmup but results are discarded.

Example#

This example references the compare example and targets Postgres and MySQL using identical schemas with 1,000 users and 5,000 orders.

Setup#

docker compose -f infra/compose_postgres.yml up -d
docker compose -f infra/compose_mysql.yml up -d

until psql "postgres://root:password@localhost:5432/defaultdb" -c "SELECT 1" &>/dev/null; do sleep 1; done
until mysql -u root -ppassword -h 127.0.0.1 -e "SELECT 1" &>/dev/null 2>&1; do sleep 1; done

All-in-one#

edg compare all \
  --a-driver pgx \
  --a-url "postgres://root:password@localhost:5432/defaultdb?sslmode=disable" \
  --a-config examples/compare/postgres.yaml \
  --b-driver mysql \
  --b-url "root:password@tcp(localhost:3306)/defaultdb?parseTime=true" \
  --b-config examples/compare/mysql.yaml \
  --rng-seed 42 \
  -w 4 \
  -d 30s \
  --tui

Step-by-step#

edg compare up \
  --a-driver pgx \
  --a-url "postgres://root:password@localhost:5432/defaultdb?sslmode=disable" \
  --a-config examples/compare/postgres.yaml \
  --b-driver mysql \
  --b-url "root:password@tcp(localhost:3306)/defaultdb?parseTime=true" \
  --b-config examples/compare/mysql.yaml

edg compare seed \
  --a-driver pgx \
  --a-url "postgres://root:password@localhost:5432/defaultdb?sslmode=disable" \
  --a-config examples/compare/postgres.yaml \
  --b-driver mysql \
  --b-url "root:password@tcp(localhost:3306)/defaultdb?parseTime=true" \
  --b-config examples/compare/mysql.yaml \
  --rng-seed 42

edg compare run \
  --a-driver pgx \
  --a-url "postgres://root:password@localhost:5432/defaultdb?sslmode=disable" \
  --a-config examples/compare/postgres.yaml \
  --b-driver mysql \
  --b-url "root:password@tcp(localhost:3306)/defaultdb?parseTime=true" \
  --b-config examples/compare/mysql.yaml \
  -w 4 \
  -d 30s \
  --tui

Teardown#

edg compare down \
  --a-driver pgx \
  --a-url "postgres://root:password@localhost:5432/defaultdb?sslmode=disable" \
  --a-config examples/compare/postgres.yaml \
  --b-driver mysql \
  --b-url "root:password@tcp(localhost:3306)/defaultdb?parseTime=true" \
  --b-config examples/compare/mysql.yaml

docker compose -f infra/compose_postgres.yml down
docker compose -f infra/compose_mysql.yml down

TUI#

Add --tui to compare run or compare all for an interactive side-by-side view.

Log view#

A single merged table with separate header rows for each database:

00:15 / 00:30

A - pgx
QUERY         COUNT   ERRORS  AVG      p50      p95      p99      QPS
lookup_user   1200    0       1.2ms    1.0ms    2.1ms    3.4ms    80.0
list_orders   1180    0       2.3ms    2.0ms    4.1ms    6.2ms    78.7
insert_order  240     0       3.1ms    2.8ms    5.2ms    7.8ms    16.0

B - mysql
QUERY         COUNT   ERRORS  AVG      p50      p95      p99      QPS
lookup_user   980     0       2.4ms    2.1ms    4.3ms    6.8ms    65.3
list_orders   960     0       3.8ms    3.4ms    6.9ms    10.1ms   64.0
insert_order  195     0       5.2ms    4.8ms    8.4ms    12.3ms   13.0

Graph view#

Press Tab to switch to the graph view. Both databases’ series are overlaid on a single chart with distinct color families. Series names are prefixed with “A:” and “B:”.

Keyboard controls#

Same as the standard TUI: Tab toggles log/graph, arrow keys navigate, 1-9 focus a series, q or Esc quits.

Text mode#

Without --tui, progress prints to stdout at each --print-interval. After the run completes, a summary shows per-query stats for both databases with delta percentages:

summary
Duration:  30.001s
Workers:   4

A - pgx
QUERY         COUNT  ERRORS  AVG      p50      p95      p99      QPS
lookup_user   2400   0       1.2ms    1.0ms    2.1ms    3.4ms    80.0
list_orders   2360   0       2.3ms    2.0ms    4.1ms    6.2ms    78.7
insert_order  480    0       3.1ms    2.8ms    5.2ms    7.8ms    16.0

B - mysql
QUERY         COUNT  ERRORS  AVG      p50      p95      p99      QPS
lookup_user   1960   0       2.4ms    2.1ms    4.3ms    6.8ms    65.3
list_orders   1920   0       3.8ms    3.4ms    6.9ms    10.1ms   64.0
insert_order  390    0       5.2ms    4.8ms    8.4ms    12.3ms   13.0

delta (B vs A)
QUERY         AVG       p50       p95       p99       QPS
lookup_user   +100.0%   +110.0%   +104.8%   +100.0%   -18.4%
list_orders   +65.2%    +70.0%    +68.3%    +62.9%    -18.7%
insert_order  +67.7%    +71.4%    +61.5%    +57.7%    -18.8%

Positive deltas in latency columns mean B is slower. Negative deltas in QPS mean B has lower throughput.

Config requirements#

Compare configs differ from sync configs in one key way: they must include a run section with benchmark queries.

  • Matching query names: Use the same name: values in both configs so results align in the TUI
  • Matching weights: Use the same weight: values so the workload mix is comparable
  • Driver-specific SQL: Adapt placeholder syntax ($1 for pgx, ? for mysql) and dialect differences (ON CONFLICT vs ON DUPLICATE KEY)
  • Same globals: Use identical globals values for consistent seeding