diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml deleted file mode 100644 index 506981c6..00000000 --- a/.github/actions/setup-rust/action.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Setup Rust - -runs: - using: composite - steps: - - - name: Install - shell: /bin/bash -l {0} - run: rustup toolchain install stable --profile minimal --no-self-update - - - name: Setup Rust cache - uses: Swatinem/rust-cache@v2 diff --git a/.github/actions/setup-test/action.yml b/.github/actions/setup-test/action.yml new file mode 100644 index 00000000..93c88459 --- /dev/null +++ b/.github/actions/setup-test/action.yml @@ -0,0 +1,22 @@ +name: Setup Test +description: | + This action sets up the environment for running tests in a Rust project. + It installs Rust, sets up caching, and installs mise. + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + - name: Install rust + shell: /bin/bash -l {0} + run: rustup toolchain install stable --profile minimal --no-self-update + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-provider: buildjet + cache-all-crates: true + - uses: jdx/mise-action@v2 + with: + version: 2025.1.0 # [default: latest] mise version to install + install: true # [default: true] run `mise install` + cache: true # [default: true] cache mise using GitHub's cache diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..7fd37064 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,59 @@ +name: Benchmark Proxy +on: + pull_request: + branches: + - main + push: + branches: + - main + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +permissions: + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write + +jobs: + benchmark: + name: Performance regression check + runs-on: buildjet-16vcpu-ubuntu-2204 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-test + - run: | + mise run postgres:up --extra-args "--detach --wait" + - name: Run benchmark + working-directory: tests/benchmark + env: + CS_WORKSPACE_ID: ${{ secrets.CS_WORKSPACE_ID }} + CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }} + CS_DEFAULT_KEYSET_ID: ${{ secrets.CS_DEFAULT_KEYSET_ID }} + CS_CLIENT_ID: ${{ secrets.CS_CLIENT_ID }} + CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }} + RUST_BACKTRACE: "1" + run: mise run benchmark:continuous + # Download previous benchmark result from cache (if exists) + - name: Download previous benchmark data + uses: actions/cache@v4 + with: + path: ./cache + key: ${{ runner.os }}-benchmark + # Run `github-action-benchmark` action + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + # What benchmark tool the output.txt came from + tool: 'customSmallerIsBetter' + # Where the output from the benchmark tool is stored + output-file-path: tests/benchmark/results/output.json + + github-token: ${{ secrets.GITHUB_TOKEN }} + fail-on-alert: true + comment-on-alert: true + summary-always: true + auto-push: true + benchmark-data-dir-path: docs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e7e361a..64c17a0c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test +name: Test on: pull_request: branches: @@ -10,25 +10,14 @@ on: env: CARGO_TERM_COLOR: always + jobs: test: name: Test runs-on: buildjet-16vcpu-ubuntu-2204 steps: - uses: actions/checkout@v4 - - name: Install rust - shell: /bin/bash -l {0} - run: rustup toolchain install stable --profile minimal --no-self-update - - name: Setup Rust cache - uses: Swatinem/rust-cache@v2 - with: - cache-provider: buildjet - cache-all-crates: true - - uses: jdx/mise-action@v2 - with: - version: 2025.1.0 # [default: latest] mise version to install - install: true # [default: true] run `mise install` - cache: true # [default: true] cache mise using GitHub's cache + - uses: ./.github/actions/setup-test - run: | mise run postgres:up --extra-args "--detach --wait" - env: diff --git a/.gitignore b/.gitignore index f0f68365..a5b17a57 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ rust-toolchain.toml # credentials for local dev .env.proxy.docker + +## benchmark result data +tests/benchmark/results/*.csv +tests/benchmark/benchmark-*.png \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index f6590553..f643795b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,13 @@ edition = "2021" incremental = true debug = true +# [profile.dev.package]# aws-lc-sys.opt-level = 3 +# proc-macro2.opt-level = 3 +# quote.opt-level = 3 +# serde_derive.opt-level = 3 +# sqlparser.opt-level = 3 +# syn.opt-level = 3 + [profile.dev.build-override] opt-level = 3 @@ -17,10 +24,15 @@ opt-level = 3 incremental = true debug = true +# Default release profile (https://doc.rust-lang.org/cargo/reference/profiles.html) [profile.release] codegen-units = 1 strip = "symbols" -lto = true + +[profile.profiling] +inherits = "release" +strip = "none" +debug = true [workspace.dependencies] sqlparser = { version = "^0.52", features = ["bigdecimal", "serde"] } diff --git a/README.md b/README.md index 1389d5e0..4278fc38 100644 --- a/README.md +++ b/README.md @@ -786,6 +786,41 @@ If the proxy is running on a host other than localhost, access on that host. | `-h`, `--help` | Displays this help message | - | +## Benchmarks + +Benchmarking is integrated into CI, and can be run locally. + +The benchmark uses `pgbench` to compare direct access to PostgreSQL against `pgbouncer` and Proxy. +Benchmarks are executed in Docker containers, as the focus is on providing a repeatable baseline for performance. +Your mileage may vary when comparing against a "real-world" production configuration. + +The benchmarks use the extended protocol option and cover: +- the default `pgbench` transaction +- insert, update & select of plaintext data +- insert, update & select of encrypted data (CipherStash Proxy only) + +The benchmark setup includes the database configuration, but does requires access to a CipherStash account environment. + +### Require environment variables +``` +CS_WORKSPACE_ID +CS_CLIENT_ACCESS_KEY +CS_DEFAULT_KEYSET_ID +CS_CLIENT_ID +CS_CLIENT_KEY +``` + + +### Running the benchmark + +```bash +cd tests/benchmark +mise run benchmark +``` + +Results are graphed in a file called `benchmark-{YmdHM}.png` where `YmdHM` is a generated timestamp. +Detailed results are generated in `csv` format and in the `results` directory. + ## More info diff --git a/mise.toml b/mise.toml index 1b571c85..3688e1ca 100644 --- a/mise.toml +++ b/mise.toml @@ -111,6 +111,15 @@ mise run test:unit mise run test:integration """ +[tasks."reset"] +alias = 'r' +description = "Reset database" +run = """ +mise run postgres:down +mise run postgres:up --extra-args "--detach --wait" +mise run postgres:setup +""" + [tasks."test:local:unit"] alias = 'lu' description = "Runs test/s" diff --git a/packages/cipherstash-proxy/Cargo.toml b/packages/cipherstash-proxy/Cargo.toml index a29c0a1d..eba235cc 100644 --- a/packages/cipherstash-proxy/Cargo.toml +++ b/packages/cipherstash-proxy/Cargo.toml @@ -56,7 +56,6 @@ uuid = { version = "1.11.0", features = ["serde", "v4"] } x509-parser = "0.17.0" vitaminc-protected = "0.1.0-pre2" - [dev-dependencies] recipher = "0.1.3" temp-env = "0.3.6" diff --git a/packages/cipherstash-proxy/src/postgresql/messages/terminate.rs b/packages/cipherstash-proxy/src/postgresql/messages/terminate.rs index 4729c478..0024c10e 100644 --- a/packages/cipherstash-proxy/src/postgresql/messages/terminate.rs +++ b/packages/cipherstash-proxy/src/postgresql/messages/terminate.rs @@ -1,6 +1,5 @@ -use bytes::{BufMut, BytesMut}; - use super::FrontendCode; +use bytes::{BufMut, BytesMut}; pub struct Terminate; diff --git a/tests/benchmark/docker/pgbouncer/Dockerfile b/tests/benchmark/docker/pgbouncer/Dockerfile new file mode 100644 index 00000000..6148a3ff --- /dev/null +++ b/tests/benchmark/docker/pgbouncer/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.19 + +RUN apk add pgbouncer +RUN adduser -D -S pgbouncer + +ADD pgbouncer.ini /etc/pgbouncer/pgbouncer.ini +ADD userlist.txt /etc/pgbouncer/userlist.txt + +EXPOSE 6433 +USER pgbouncer +CMD /usr/bin/pgbouncer /etc/pgbouncer/pgbouncer.ini diff --git a/tests/benchmark/docker/pgbouncer/pgbouncer.ini b/tests/benchmark/docker/pgbouncer/pgbouncer.ini new file mode 100644 index 00000000..6438d731 --- /dev/null +++ b/tests/benchmark/docker/pgbouncer/pgbouncer.ini @@ -0,0 +1,24 @@ +[databases] +cipherstash = host=host.docker.internal port=5532 dbname=cipherstash user=cipherstash password=password + +[users] + +[pgbouncer] +listen_addr = * +listen_port = 6433 +listen_backlog = 1000 +so_reuseport = 1 + +default_pool_size = 100 +min_pool_size = 20 + +max_db_connections = 100 +max_client_conn = 1000 + +syslog = 0 +log_connections = 0 +log_disconnections = 0 +verbose = 0 + +auth_type = scram-sha-256 +auth_file = /etc/pgbouncer/userlist.txt diff --git a/tests/benchmark/docker/pgbouncer/userlist.txt b/tests/benchmark/docker/pgbouncer/userlist.txt new file mode 100644 index 00000000..e479d766 --- /dev/null +++ b/tests/benchmark/docker/pgbouncer/userlist.txt @@ -0,0 +1 @@ +"cipherstash" "password" diff --git a/tests/benchmark/docker/pgcat/pgcat.toml b/tests/benchmark/docker/pgcat/pgcat.toml new file mode 100644 index 00000000..0c2f9201 --- /dev/null +++ b/tests/benchmark/docker/pgcat/pgcat.toml @@ -0,0 +1,111 @@ +# +# PgCat config example. +# + +# +# General pooler settings +[general] +# What IP to run on, 0.0.0.0 means accessible from everywhere. +host = "0.0.0.0" + +# Port to run on, same as PgBouncer used in this example. +port = 6434 + +# Whether to enable prometheus exporter or not. +enable_prometheus_exporter = true + +# Port at which prometheus exporter listens on. +prometheus_exporter_port = 9930 + +# How long to wait before aborting a server connection (ms). +connect_timeout = 5000 + +# How much time to give `SELECT 1` health check query to return with a result (ms). +healthcheck_timeout = 1000 + +# How long to keep connection available for immediate re-use, without running a healthcheck query on it +healthcheck_delay = 30000 + +# How much time to give clients during shutdown before forcibly killing client connections (ms). +shutdown_timeout = 60000 + +# For how long to ban a server if it fails a health check (seconds). +ban_time = 60 # seconds + +# If we should log client connections +log_client_connections = false + +# If we should log client disconnections +log_client_disconnections = false + +# TLS +# tls_certificate = "server.cert" +# tls_private_key = "server.key" + +# Credentials to access the virtual administrative database (pgbouncer or pgcat) +# Connecting to that database allows running commands like `SHOW POOLS`, `SHOW DATABASES`, etc.. +admin_username = "cipherstash" +admin_password = "password" + +# pool +# configs are structured as pool. +# the pool_name is what clients use as database name when connecting +# For the example below a client can connect using "postgres://sharding_user:sharding_user@pgcat_host:pgcat_port/sharded" +[pools.cipherstash] +# Pool mode (see PgBouncer docs for more). +# session: one server connection per connected client +# transaction: one server connection per client transaction +pool_mode = "session" + +# If the client doesn't specify, route traffic to +# this role by default. +# +# any: round-robin between primary and replicas, +# replica: round-robin between replicas only without touching the primary, +# primary: all queries go to the primary unless otherwise specified. +default_role = "any" + +# Query parser. If enabled, we'll attempt to parse +# every incoming query to determine if it's a read or a write. +# If it's a read query, we'll direct it to a replica. Otherwise, if it's a write, +# we'll direct it to the primary. +query_parser_enabled = false + +# If the query parser is enabled and this setting is enabled, we'll attempt to +# infer the role from the query itself. +query_parser_read_write_splitting = false + +# If the query parser is enabled and this setting is enabled, the primary will be part of the pool of databases used for +# load balancing of read queries. Otherwise, the primary will only be used for write +# queries. The primary can always be explicitly selected with our custom protocol. +primary_reads_enabled = true + +# So what if you wanted to implement a different hashing function, +# or you've already built one and you want this pooler to use it? +# +# Current options: +# +# pg_bigint_hash: PARTITION BY HASH (Postgres hashing function) +# sha1: A hashing function based on SHA1 +# +sharding_function = "pg_bigint_hash" + +# Credentials for users that may connect to this cluster +[pools.cipherstash.users.0] +username = "cipherstash" +password = "password" +# Maximum number of server connections that can be established for this user +# The maximum number of connection from a single Pgcat process to any database in the cluster +# is the sum of pool_size across all users. +pool_size = 100 +min_pool_size = 20 + +# Maximum query duration. Dangerous, but protects against DBs that died in a non-obvious way. +statement_timeout = 0 + +# Shard 0 +[pools.cipherstash.shards.0] + +servers = [["host.docker.internal", 5532, "primary"]] + +database = "cipherstash" diff --git a/tests/benchmark/mise.toml b/tests/benchmark/mise.toml new file mode 100644 index 00000000..69d2f2bb --- /dev/null +++ b/tests/benchmark/mise.toml @@ -0,0 +1,174 @@ +[settings] + +trusted_config_paths = ["./env/mise.passthrough.toml"] + +[task_config] +includes = ["./tasks"] + + +[env] + +POSTGRES_DB = "{{env.CS_DATABASE__NAME}}" +PGUSER = "{{env.CS_DATABASE__USERNAME}}" +PGPASSWORD = "{{env.CS_DATABASE__PASSWORD}}" +PGHOST = "{{env.CS_DATABASE__HOST}}" +PGPORT = "{{env.CS_DATABASE__PORT}}" + +# ==================================================================================================== + +[tasks."benchmark:continuous"] +description = "Run proxy encryption benchmark for CI" +run = """ +set -e + +echo +echo '###############################################' +echo '# Preflight' +echo '###############################################' +echo + +mise run benchmark:clean + +# Ensure Postgres instances are running +mise run test:integration:preflight + +echo +echo '###############################################' +echo '# Setup' +echo '###############################################' +echo + +# Ensure EQL is set up before we try and start Proxy +mise --env tcp run postgres:setup + +mise run benchmark:setup + +mise --env tcp run proxy:up proxy --extra-args "--detach --wait" +mise --env tcp run test:wait_for_postgres_to_quack --port 6432 --max-retries 20 + +echo +echo '###############################################' +echo '# Extended protocol with encrypted transaction' +echo '###############################################' +echo + +# Extended protocol with encrypted script +mise run benchmark_service --target=proxy --transaction=encrypted --protocol=extended --port=6432 --time=30 --clients=10 +""" + + +[tasks."benchmark"] +alias = 'b' +description = "Run benchmarks" +run = """ +set -e + +mise run benchmark:clean + +set -e + +echo +echo '###############################################' +echo '# Preflight' +echo '###############################################' +echo + +# Ensure Postgres instances are running +mise run test:integration:preflight + +echo +echo '###############################################' +echo '# Setup' +echo '###############################################' +echo + +# Ensure EQL is set up before we try and start Proxy +mise --env tcp run postgres:setup + +mise run benchmark:setup + +mise --env tcp run pgbouncer:up --extra-args "--detach --wait" +mise --env tcp run pgcat:up --extra-args "--detach --wait" +mise --env tcp run proxy:up proxy --extra-args "--detach --wait" +mise --env tcp run test:wait_for_postgres_to_quack --port 6432 --max-retries 20 + + +echo +echo '###############################################' +echo '# Extended protocol with default pgbench transaction' +echo '###############################################' +echo + +# # Extended protocol with default script +mise run benchmark_service --target=postgres --protocol=extended --port=5532 --time=5 +mise run benchmark_service --target=pgbouncer --protocol=extended --port=6433 --time=5 +mise run benchmark_service --target=pgcat --protocol=extended --port=6434 --time=5 +mise run benchmark_service --target=proxy --protocol=extended --port=6432 --time=5 + + +echo +echo '###############################################' +echo '# Extended protocol with plaintext transaction' +echo '###############################################' +echo + +mise run benchmark_service --target=postgres --transaction=plaintext --protocol=extended --port=5532 --time=5 +mise run benchmark_service --target=pgbouncer --transaction=plaintext --protocol=extended --port=6433 --time=5 +mise run benchmark_service --target=pgcat --transaction=plaintext --protocol=extended --port=6434 --time=5 +mise run benchmark_service --target=proxy --transaction=plaintext --protocol=extended --port=6432 --time=5 + + +echo +echo '###############################################' +echo '# Extended protocol with encrypted transaction' +echo '###############################################' +echo + +# Extended protocol with encrypted script +mise run benchmark_service --target=proxy --transaction=encrypted --protocol=extended --port=6432 --time=5 + + +echo +echo '###############################################' +echo '# Process results' +echo '###############################################' +echo + +mise run benchmark:plot +""" + +[tasks."benchmark:setup"] +run = """ +cat sql/benchmark-schema.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f- + +# Initialize pgbench +docker compose run --rm postgres${CONTAINER_SUFFIX:-} pgbench --host=host.docker.internal --port=${CS_DATABASE__PORT} --scale=1 -i --no-vacuum +""" + +[tasks."benchmark:plot"] +alias = 'b' +description = "Plot graphs from benchmark results" +run = """ +python plot.py +""" + +[tasks."benchmark:clean"] +description = "Clean old benchmark results" +run = """ +rm -rf {{config_root}}/results/*.csv +""" + +[tasks."pgbouncer:up"] +description = "Run pgbouncer" +run = """ +set -e +echo docker compose up --build {{arg(name="service",default="pgbouncer")}} {{option(name="extra-args",default="")}} | bash +""" + + +[tasks."pgcat:up"] +description = "Run pgcat" +run = """ +set -e +echo docker compose up --build {{arg(name="service",default="pgcat")}} {{option(name="extra-args",default="")}} | bash +""" diff --git a/tests/benchmark/plot.py b/tests/benchmark/plot.py new file mode 100644 index 00000000..36592669 --- /dev/null +++ b/tests/benchmark/plot.py @@ -0,0 +1,118 @@ +import csv +import glob +import matplotlib.pyplot as plt +import time + + +MARKERS = { + "proxy-extended-default": "*", + "proxy-extended-plaintext": "*", + "proxy-extended-encrypted": "*", + + "postgres-extended-default": ".", + "postgres-extended-plaintext": ".", + + "pgcat-extended-default": "o", + "pgcat-extended-plaintext": "o", + + "pgbouncer-extended-default": "s", + "pgbouncer-extended-plaintext": "s", +} + +LINES = { + "proxy-extended-default": ":", + "proxy-extended-plaintext": "--", + "proxy-extended-encrypted": "-", + + "postgres-extended-default": ":", + "postgres-extended-plaintext": "--", + + "pgcat-extended-default": "-", + "pgcat-extended-plaintext": "--", + + "pgbouncer-extended-default": "-", + "pgbouncer-extended-plaintext": "--", +} + +COLORS = { + "proxy-extended-default": "xkcd:grey", + "proxy-extended-plaintext": "xkcd:bright blue", + "proxy-extended-encrypted": "xkcd:red orange", + + "postgres-extended-default": "xkcd:grey", + "postgres-extended-plaintext": "xkcd:blue", + + "pgcat-extended-default": "xkcd:grey", + "pgcat-extended-plaintext": "xkcd:ocean blue", + + "pgbouncer-extended-default": "xkcd:grey", + "pgbouncer-extended-plaintext": "xkcd:cornflower blue", +} + + +def read_csv(file_name): + with open(file_name) as csv_file: + csv_reader = csv.DictReader(csv_file) + + rows = [] + for csv_line in csv_reader: + try: + row = { + "clients": int(csv_line["clients"]), + "latency": float(csv_line["latency"]), + "init_conn_time": float(csv_line["init_conn_time"]), + "tps": float(csv_line["tps"]), + } + rows.append(row) + except: + print("Unable to parse row from csv_line:", csv_line) + + return rows + + +def main(): + fig, ax = plt.subplots(figsize=(16, 12), layout="constrained") + + files = [f for f in glob.glob("results/*.csv")] + files = sorted(files) + + for i, file in enumerate(files): + label = file.replace(".csv", "") + label = label.replace("results/", "") + + marker = MARKERS[label] + line = LINES[label] + color = COLORS[label] + + data = read_csv(file) + clients = [d["clients"] for d in data] + tps = [d["tps"] for d in data] + + ax.plot( + clients, + tps, + label=label, + marker=marker, + linestyle=line, + color=color, + markeredgewidth=1, + markersize=10, + ) + + # Ensure the baseline starts at zero + ax.set_xlim(left=0) # Set x-axis lower limit to 0 + ax.set_ylim(bottom=0) # Set y-axis lower limit to 0 + + + fig.legend(loc="outside upper left") + plt.xlabel("clients") + plt.ylabel("tps") + plt.title("Transactions per second") + + ts = time.strftime("%Y%m%d%H%M") + file_name = "benchmark-{}.png".format(ts) + plt.savefig(file_name) + + +if __name__ == "__main__": + main() diff --git a/tests/benchmark/sql/benchmark-schema.sql b/tests/benchmark/sql/benchmark-schema.sql new file mode 100644 index 00000000..fecdf7bf --- /dev/null +++ b/tests/benchmark/sql/benchmark-schema.sql @@ -0,0 +1,23 @@ +TRUNCATE TABLE cs_configuration_v1; + +DROP TABLE IF EXISTS benchmark_plaintext; +CREATE TABLE benchmark_plaintext ( + id serial primary key, + username text, + email text +); + +DROP TABLE IF EXISTS benchmark_encrypted; +CREATE TABLE benchmark_encrypted ( + id serial primary key, + username text, + email cs_encrypted_v1 +); + +SELECT cs_add_column_v1( + 'benchmark_encrypted', + 'email' +); + +SELECT cs_encrypt_v1(); +SELECT cs_activate_v1(); diff --git a/tests/benchmark/sql/transaction-default.sql b/tests/benchmark/sql/transaction-default.sql new file mode 100644 index 00000000..eede3bfa --- /dev/null +++ b/tests/benchmark/sql/transaction-default.sql @@ -0,0 +1,11 @@ +\set aid random(1, 100000 * :scale) +\set bid random(1, 1 * :scale) +\set tid random(1, 10 * :scale) +\set delta random(-5000, 5000) +BEGIN; +UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; +SELECT abalance FROM pgbench_accounts WHERE aid = :aid; +UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; +UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; +INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); +END; \ No newline at end of file diff --git a/tests/benchmark/sql/transaction-encrypted.sql b/tests/benchmark/sql/transaction-encrypted.sql new file mode 100644 index 00000000..b1409c61 --- /dev/null +++ b/tests/benchmark/sql/transaction-encrypted.sql @@ -0,0 +1,24 @@ + + +\set a random(1, 100000) +\set b random(1, 100000) +\set c random(1, 100000) + +\set username hash_fnv1a(:a) + +\set email hash_fnv1a(:b) + +\set email_update hash_fnv1a(:c) + +BEGIN; + +INSERT INTO benchmark_encrypted(username, email) VALUES (:username, :email); + +SELECT username FROM benchmark_encrypted WHERE email = :email; + +UPDATE benchmark_encrypted SET email = :email_update WHERE username = :username; + +SELECT username FROM benchmark_encrypted WHERE email = :email_update; + +END; + diff --git a/tests/benchmark/sql/transaction-plaintext.sql b/tests/benchmark/sql/transaction-plaintext.sql new file mode 100644 index 00000000..c2eda08e --- /dev/null +++ b/tests/benchmark/sql/transaction-plaintext.sql @@ -0,0 +1,23 @@ +\set a random(1, 100000) +\set b random(1, 100000) +\set c random(1, 100000) + +\set username hash_fnv1a(:a) + +\set email hash_fnv1a(:b) + +\set email_update hash_fnv1a(:c) + + +BEGIN; + +INSERT INTO benchmark_plaintext(username, email) VALUES (:username, :email); + +SELECT username FROM benchmark_plaintext WHERE email = :email; + +UPDATE benchmark_plaintext SET email = :email_update WHERE username = :username; + +SELECT username FROM benchmark_plaintext WHERE email = :email_update; + +END; + diff --git a/tests/benchmark/tasks/benchmark_service.sh b/tests/benchmark/tasks/benchmark_service.sh new file mode 100755 index 00000000..e8f52f13 --- /dev/null +++ b/tests/benchmark/tasks/benchmark_service.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +#MISE description="Run pgbench against a single target" +#USAGE flag "--host " default="host.docker.internal" help="Host of target service" +#USAGE flag "--port " help="Port of target service" +#USAGE flag "--time