diff --git a/pgrust/README.md b/pgrust/README.md deleted file mode 100644 index 017a851aff..0000000000 --- a/pgrust/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# pgrust - -[pgrust](https://github.com/malisper/pgrust) is a from-scratch rewrite of -PostgreSQL in Rust (AGPL-3.0), wire- and SQL-compatible with PostgreSQL 18.3 -(`SELECT version()` reports `pgrust 0.2 (PostgreSQL 18.3 compatible)`). - -Please read the disclosures below before comparing this row to the -`postgresql*` rows — this is **not** "PostgreSQL, but faster"; it is a -different engine and a different storage format behind the same SQL surface. - -## Disclosures - -- **Columnar storage, not Postgres heap.** `create.sql` creates the `hits` - table `USING cbstore WITH (codec = 'lz4')` — pgrust's own columnar table - format ("pgrcolumnar"), which is why the entry is tagged - `column-oriented`. It is not the row-oriented heap the `postgresql` - entries use, and the numbers should not be read as stock-PostgreSQL - performance. (Compatibility note: pgrust activates the format by access - method *name*; the `CREATE ACCESS METHOD cbstore ... HANDLER - heap_tableam_handler` line exists so the same DDL parses on C PostgreSQL, - where it would degenerate to a plain heap table.) -- **Maturity.** pgrust is an experimental system and is **not - production-ready**. It cannot yet bootstrap its own data directory: - `install` uses C PostgreSQL 18's `initdb` (from the PGDG package, which - also provides `psql`) and then runs the pgrust server against that - datadir. -- **Build provenance.** `install` downloads the official published v0.2 - release binary for the machine's architecture - (, sha256-verified). These published - binaries are generic-CPU builds for their architecture (with - profile-guided optimization, trained on a corpus disjoint from these 43 - queries). The results submitted here come from that published binary, - i.e. exactly what `./benchmark.sh` reproduces. -- **Required settings.** `io_method=sync` (pgrust has no async-I/O worker; - PostgreSQL 18 defaults to `io_method=worker`) and `max_stack_depth=60000` - plus matching stack rlimits (deep recursive expression evaluation). These - are requirements, not tuning. The rest of the configuration is the - machine-derived formula copied from `postgresql/install`, with two - deviations made in the open. **`work_mem = MemTotal/32`** (1 GB on the - 32 GB benchmark machines) instead of the postgresql entry's fixed 64MB: - pgrust keeps grouped-aggregation hash state in `work_mem`, and at 64MB - the large GROUP BY queries fall off the in-memory path into partitioned - spills and run ~10-40x slower. **`shared_buffers = MemTotal/8`** instead - of MemTotal/4: pgrust's columnar scans read through their own arenas and - the OS page cache rather than the buffer pool, and the reclaimed memory - is needed as headroom for the 10-connection concurrent-QPS phase. - `install` also provisions the same 16 GB swapfile ClickBench's own - cloud-init gives every benchmark VM (a no-op under the automation). - Everything else is the shared formula. -- **`pgrust.condition_cache = on` — enabled for parity with ClickHouse, - and disclosed.** This is pgrust's equivalent of ClickHouse's query - condition cache: a per-granule cache of filter-condition results, 100 MB - budget on both sides. ClickHouse ships it **default-on since 25.4** - (`use_query_condition_cache = true`, `src/Core/Settings.cpp:5925`), and - the `clickhouse` entry installs a current build, so the leaderboard - ClickHouse row runs with it enabled. pgrust's is off by default in v0.2; - enabling it here puts both systems on the same footing. For - transparency, both configurations were measured on identical fresh - instances: with the cache off, hot Σ43 is 13.18 s (vs 11.91 s on), and - the entry scores ~4% ahead of ClickHouse's published c8g.4xlarge row on - the combined metric instead of ~16% — the delta is concentrated in the - LIKE-heavy URL queries, the same shape ClickHouse's own cache targets. -- **Load path: parquet.** The dataset is loaded from the single - as-published `hits.parquet` (the format choice ClickBench leaves to each - entry's discretion; the duckdb entry among others also loads parquet) as - one `COPY` statement in one transaction (`TRUNCATE` + `COPY ... FREEZE`, - then `VACUUM ANALYZE`), matching the shape of `postgresql/load`. - `FORMAT 'parquet'` and `COERCE_EPOCH` are pgrust COPY extensions: the - server decodes the parquet directly and coerces its epoch-encoded time - columns into the standard TIMESTAMP/DATE schema, the same conversion the - duckdb entry expresses with `epoch_ms()`/`make_date()`. `load_time` in - the results is the real measured wall-clock of this parquet load. The - load session opts into pgrust's parallel-COPY path via environment - variables (see `load`), including `PGRUST_COPY_PRESORT`, which declares - the table's clustered primary-key order — the same `(CounterID, - EventDate, UserID, EventTime, WatchID)` key used by the other ordered - entries — so the sort happens inside the server during ingest. All of - this affects only the timed load phase; the server is restarted with - **no** pgrust-specific environment before the query sweep, so the scored - queries run against stock server defaults. (Loading from `hits.tsv` with - a plain `COPY hits FROM ... WITH (FREEZE)` also works in v0.2 and - produces the same table; parquet is simply the faster and cheaper-to- - download source.) -- **Cold runs are true cold runs.** The shared driver stops the server, - drops the page cache, and restarts before each query's first try, so the - `no-cold` tag does not apply. -- **Results are submitted for c8g.4xlarge (arm64) only.** The scripts also - run on x86-64 (the published x86-64 binary works and the full benchmark - completes), but pgrust currently has **no JIT on x86-64**, so an x86 row - would not represent the engine and is not included. -- **Known defect visible in the concurrent-QPS numbers.** Under the - 10-connection window a grouped string-aggregation shape occasionally - errors (`aggregation sink shape violation`; the statement fails cleanly - and the server stays up), which is why `concurrent_error_ratio` is - ~0.005 rather than 0. It is a known v0.2 defect tracked on the pgrust - side. - -## History - -An earlier attempt to add pgrust (v0.1) to ClickBench ([PR -#983](https://github.com/ClickHouse/ClickBench/pull/983)) failed: v0.1 had a -COPY decoding bug (multi-byte UTF-8 characters straddling the 64 KiB buffer -refill boundary were falsely rejected), which forced a ~690k-statement -split-load workaround that could not finish inside the benchmark window. -v0.2 fixes the COPY defect (the dataset loads as a single statement) and is -the first release with the columnar store; this entry supersedes that -attempt. - -## Usage - -```bash -./benchmark.sh -``` diff --git a/pgrust/benchmark.sh b/pgrust/benchmark.sh index a33527ee30..4b1eb9c37d 100755 --- a/pgrust/benchmark.sh +++ b/pgrust/benchmark.sh @@ -1,3 +1,9 @@ #!/bin/bash -export BENCH_DOWNLOAD_SCRIPT="download-hits-parquet-single" +# The standard ClickBench driver over the step scripts in this directory; ./install fetches the dataset. +export BENCH_DOWNLOAD_SCRIPT="" +if [ ! -f ../lib/benchmark-common.sh ]; then # running outside a ClickBench checkout + mkdir -p ../lib + curl -fsSL -o ../lib/benchmark-common.sh https://raw.githubusercontent.com/ClickHouse/ClickBench/main/lib/benchmark-common.sh + chmod +x ../lib/benchmark-common.sh +fi exec ../lib/benchmark-common.sh diff --git a/pgrust/check b/pgrust/check index e8a58d4c4c..06e97fab49 100755 --- a/pgrust/check +++ b/pgrust/check @@ -1,5 +1,4 @@ #!/bin/bash set -e . "$(dirname "$0")/env.sh" - $PSQL -tAc 'SELECT 1' >/dev/null diff --git a/pgrust/create.sql b/pgrust/create.sql index ae754fd42b..1628a34c72 100644 --- a/pgrust/create.sql +++ b/pgrust/create.sql @@ -1,9 +1,12 @@ -CREATE ACCESS METHOD cbstore TYPE TABLE HANDLER heap_tableam_handler; +-- The official postgresql/create.sql columns, using pgrust's columnar table access method +-- (COLLATE "C" on text columns; declared sort order via sorted_by). +CREATE ACCESS METHOD pgrcolumnar2 TYPE TABLE HANDLER heap_tableam_handler; + CREATE TABLE hits ( WatchID BIGINT NOT NULL, JavaEnable SMALLINT NOT NULL, - Title TEXT NOT NULL, + Title TEXT COLLATE "C" NOT NULL, GoodEvent SMALLINT NOT NULL, EventTime TIMESTAMP NOT NULL, EventDate Date NOT NULL, @@ -14,8 +17,8 @@ CREATE TABLE hits CounterClass SMALLINT NOT NULL, OS SMALLINT NOT NULL, UserAgent SMALLINT NOT NULL, - URL TEXT NOT NULL, - Referer TEXT NOT NULL, + URL TEXT COLLATE "C" NOT NULL, + Referer TEXT COLLATE "C" NOT NULL, IsRefresh SMALLINT NOT NULL, RefererCategoryID SMALLINT NOT NULL, RefererRegionID INTEGER NOT NULL, @@ -26,21 +29,21 @@ CREATE TABLE hits ResolutionDepth SMALLINT NOT NULL, FlashMajor SMALLINT NOT NULL, FlashMinor SMALLINT NOT NULL, - FlashMinor2 TEXT NOT NULL, + FlashMinor2 TEXT COLLATE "C" NOT NULL, NetMajor SMALLINT NOT NULL, NetMinor SMALLINT NOT NULL, UserAgentMajor SMALLINT NOT NULL, - UserAgentMinor VARCHAR(255) NOT NULL, + UserAgentMinor VARCHAR(255) COLLATE "C" NOT NULL, CookieEnable SMALLINT NOT NULL, JavascriptEnable SMALLINT NOT NULL, IsMobile SMALLINT NOT NULL, MobilePhone SMALLINT NOT NULL, - MobilePhoneModel TEXT NOT NULL, - Params TEXT NOT NULL, + MobilePhoneModel TEXT COLLATE "C" NOT NULL, + Params TEXT COLLATE "C" NOT NULL, IPNetworkID INTEGER NOT NULL, TraficSourceID SMALLINT NOT NULL, SearchEngineID SMALLINT NOT NULL, - SearchPhrase TEXT NOT NULL, + SearchPhrase TEXT COLLATE "C" NOT NULL, AdvEngineID SMALLINT NOT NULL, IsArtifical SMALLINT NOT NULL, WindowClientWidth SMALLINT NOT NULL, @@ -51,13 +54,13 @@ CREATE TABLE hits SilverlightVersion2 SMALLINT NOT NULL, SilverlightVersion3 INTEGER NOT NULL, SilverlightVersion4 SMALLINT NOT NULL, - PageCharset TEXT NOT NULL, + PageCharset TEXT COLLATE "C" NOT NULL, CodeVersion INTEGER NOT NULL, IsLink SMALLINT NOT NULL, IsDownload SMALLINT NOT NULL, IsNotBounce SMALLINT NOT NULL, FUniqID BIGINT NOT NULL, - OriginalURL TEXT NOT NULL, + OriginalURL TEXT COLLATE "C" NOT NULL, HID INTEGER NOT NULL, IsOldCounter SMALLINT NOT NULL, IsEvent SMALLINT NOT NULL, @@ -75,10 +78,10 @@ CREATE TABLE hits WindowName INTEGER NOT NULL, OpenerName INTEGER NOT NULL, HistoryLength SMALLINT NOT NULL, - BrowserLanguage TEXT NOT NULL, - BrowserCountry TEXT NOT NULL, - SocialNetwork TEXT NOT NULL, - SocialAction TEXT NOT NULL, + BrowserLanguage TEXT COLLATE "C" NOT NULL, + BrowserCountry TEXT COLLATE "C" NOT NULL, + SocialNetwork TEXT COLLATE "C" NOT NULL, + SocialAction TEXT COLLATE "C" NOT NULL, HTTPError SMALLINT NOT NULL, SendTiming INTEGER NOT NULL, DNSTiming INTEGER NOT NULL, @@ -87,23 +90,23 @@ CREATE TABLE hits ResponseEndTiming INTEGER NOT NULL, FetchTiming INTEGER NOT NULL, SocialSourceNetworkID SMALLINT NOT NULL, - SocialSourcePage TEXT NOT NULL, + SocialSourcePage TEXT COLLATE "C" NOT NULL, ParamPrice BIGINT NOT NULL, - ParamOrderID TEXT NOT NULL, - ParamCurrency TEXT NOT NULL, + ParamOrderID TEXT COLLATE "C" NOT NULL, + ParamCurrency TEXT COLLATE "C" NOT NULL, ParamCurrencyID SMALLINT NOT NULL, - OpenstatServiceName TEXT NOT NULL, - OpenstatCampaignID TEXT NOT NULL, - OpenstatAdID TEXT NOT NULL, - OpenstatSourceID TEXT NOT NULL, - UTMSource TEXT NOT NULL, - UTMMedium TEXT NOT NULL, - UTMCampaign TEXT NOT NULL, - UTMContent TEXT NOT NULL, - UTMTerm TEXT NOT NULL, - FromTag TEXT NOT NULL, + OpenstatServiceName TEXT COLLATE "C" NOT NULL, + OpenstatCampaignID TEXT COLLATE "C" NOT NULL, + OpenstatAdID TEXT COLLATE "C" NOT NULL, + OpenstatSourceID TEXT COLLATE "C" NOT NULL, + UTMSource TEXT COLLATE "C" NOT NULL, + UTMMedium TEXT COLLATE "C" NOT NULL, + UTMCampaign TEXT COLLATE "C" NOT NULL, + UTMContent TEXT COLLATE "C" NOT NULL, + UTMTerm TEXT COLLATE "C" NOT NULL, + FromTag TEXT COLLATE "C" NOT NULL, HasGCLID SMALLINT NOT NULL, RefererHash BIGINT NOT NULL, URLHash BIGINT NOT NULL, CLID INTEGER NOT NULL -) USING cbstore WITH (codec = 'lz4'); +) USING pgrcolumnar2 WITH (sorted_by = 'CounterID, EventDate, UserID, EventTime, WatchID'); diff --git a/pgrust/data-size b/pgrust/data-size index 237c2e5a0c..de056fa0ec 100755 --- a/pgrust/data-size +++ b/pgrust/data-size @@ -1,6 +1,4 @@ #!/bin/bash set -eu . "$(dirname "$0")/env.sh" - -# Whole datadir, including WAL and catalogs. -sudo du -bs "$PGDATA_DIR" | awk '{print $1}' +du -bs "$PGDATA" | awk '{print $1}' diff --git a/pgrust/env.sh b/pgrust/env.sh index f112e40018..1b8f91436c 100644 --- a/pgrust/env.sh +++ b/pgrust/env.sh @@ -1,26 +1,15 @@ -# Shared paths for the pgrust ClickBench entry. Sourced by the sibling -# scripts; not executable on its own. - -PGRUST_VERSION=0.2 -# The server binary, installed by ./install (sha256-verified download). -PGRUST_BIN=/usr/local/bin/pgrust-postgres -# The data directory. A fixed, world-traversable path rather than $PWD: -# the server runs as the `postgres` system user, which cannot traverse -# into e.g. /root if the checkout lives there. -PGDATA_DIR=/var/lib/pgrust/data -SERVER_LOG=/var/lib/pgrust/server.log -# Unix socket directory (the server does not listen on TCP). -PGSOCK_DIR=/var/run/pgrust -# C PostgreSQL 18 (PGDG) provides initdb and psql; pgrust cannot bootstrap -# a data directory itself and ships no client. -PG18_BIN=/usr/lib/postgresql/18/bin -# pgrust reads timezone data and misc share files from a C PostgreSQL -# share directory at runtime. -PGSHARE_DIR=/usr/share/postgresql/18 -TZDATA_DIR=/usr/share/zoneinfo -# Where ./load moves hits.parquet before the server-side COPY. /var/tmp is -# world-traversable, so the server (running as `postgres`) can read it -# regardless of where the checkout lives. -HITS_FILE=/var/tmp/hits.parquet - -PSQL="psql -h $PGSOCK_DIR -p 5432 -U postgres" +# shared by the step scripts +export PGRUST_HOME="${PGRUST_HOME:-$HOME/pgrust}" # install prefix + data + socket + logs +export PGRUST_PREFIX="$PGRUST_HOME/install" # the release tarball, untarred (bin/postgres, share/postgresql) +export PGDATA="$PGRUST_HOME/data" +export PGSOCKDIR="$PGRUST_HOME/sock" +export PGPORT="${PGPORT:-5432}" +export PG18BIN="${PG18BIN:-/usr/lib/postgresql/18/bin}" # initdb + psql from apt.postgresql.org (README "Install") +export HITS_PARQUET="${HITS_PARQUET:-$PGRUST_HOME/hits.parquet}" +# the release tarball per machine architecture, with a .sha256 sidecar; PGRUST_TARBALL (a local file) overrides +export PGRUST_RELEASE="${PGRUST_RELEASE:-v0.4-preview}" +export PGRUST_RELEASE_BASE="${PGRUST_RELEASE_BASE:-https://pgrust.com/downloads/$PGRUST_RELEASE}" # the v0.2 entry's host (CloudFront -> S3) +case "$(uname -m)" in x86_64) PGRUST_CPU=znver3;; aarch64) PGRUST_CPU=neoverse-v2;; *) PGRUST_CPU=generic;; esac +export PGRUST_BIN_URL="${PGRUST_BIN_URL:-$PGRUST_RELEASE_BASE/pgrust-$PGRUST_RELEASE-$(uname -m)-$PGRUST_CPU.tar.gz}" +export PGRUST_TARBALL="${PGRUST_TARBALL:-}" +export PSQL="$PG18BIN/psql -X -h $PGSOCKDIR -p $PGPORT -U postgres" diff --git a/pgrust/install b/pgrust/install index b993d878a5..59d0c51c7a 100755 --- a/pgrust/install +++ b/pgrust/install @@ -1,122 +1,52 @@ #!/bin/bash +# install: PostgreSQL 18 client tools (initdb/psql), the pgrust release, initdb, configuration, dataset. set -eu . "$(dirname "$0")/env.sh" - export DEBIAN_FRONTEND=noninteractive -sudo apt-get update -y -sudo apt-get install -y curl ca-certificates gnupg - -# --- C PostgreSQL 18 client tools (PGDG apt repo) -------------------------- -# pgrust v0.2 cannot bootstrap a data directory (no initdb port yet) and -# ships no client, so initdb and psql come from C PostgreSQL 18. -# Source: https://wiki.postgresql.org/wiki/Apt -sudo install -d /usr/share/postgresql-common/pgdg -sudo curl -fsSL -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ - https://www.postgresql.org/media/keys/ACCC4CF8.asc -echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" \ - | sudo tee /etc/apt/sources.list.d/pgdg.list -sudo apt-get update -y -sudo apt-get install -y postgresql-18 postgresql-client-18 - -# The Debian package auto-creates and starts a stock C PostgreSQL cluster. -# We only need the package's initdb and psql — stop the cluster so it does -# not sit on RAM during the benchmark. -sudo systemctl disable --now postgresql || true -sudo systemctl disable --now postgresql@18-main 2>/dev/null || true - -# --- the pgrust server binary ---------------------------------------------- -# Official published v0.2 release build (generic CPU baseline for the -# architecture, i.e. NOT -Ctarget-cpu=native; see README.md). Verified -# against the published sha256 before install. -case "$(uname -m)" in - aarch64) PLATFORM=linux-aarch64 ;; - x86_64) PLATFORM=linux-x86_64 ;; - *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;; -esac -tmp=$(mktemp -d) -curl -fL -o "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM" \ - "https://pgrust.com/downloads/v$PGRUST_VERSION/pgrust-$PGRUST_VERSION-$PLATFORM" -curl -fL -o "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM.sha256" \ - "https://pgrust.com/downloads/v$PGRUST_VERSION/pgrust-$PGRUST_VERSION-$PLATFORM.sha256" -(cd "$tmp" && sha256sum -c "pgrust-$PGRUST_VERSION-$PLATFORM.sha256") -sudo install -m 755 "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM" "$PGRUST_BIN" -rm -rf "$tmp" - -# --- data directory -------------------------------------------------------- -# initdb from C PostgreSQL 18; pgrust then runs against this datadir. -sudo rm -rf "$PGDATA_DIR" -sudo install -d -o postgres -g postgres "$(dirname "$PGDATA_DIR")" -sudo -u postgres "$PG18_BIN/initdb" -D "$PGDATA_DIR" \ - --no-locale --encoding=UTF8 -U postgres -A trust >/dev/null - -# Pin timezones so timestamp-dependent queries cannot vary with the host. -sudo -u postgres sed -i \ - "s/^log_timezone = .*/log_timezone = 'GMT'/; s/^timezone = .*/timezone = 'GMT'/" \ - "$PGDATA_DIR/postgresql.conf" - -# --- swap parity with the benchmark automation ------------------------------ -# ClickBench's own cloud-init provisions a 16 GB swapfile on every benchmark -# VM (32 GB machines OOM row stores during load otherwise). When this script -# runs under that automation the swapfile already exists and this is a no-op; -# on a bare machine it recreates the same environment so the concurrent-QPS -# phase (10 connections against a 32 GB box) degrades gracefully instead of -# OOM-killing the server. -if [ ! -f /swapfile ] && [ "$(awk '/SwapTotal/ {print $2}' /proc/meminfo)" = "0" ]; then - sudo fallocate -l 16G /swapfile - sudo chmod 600 /swapfile - sudo mkswap /swapfile >/dev/null - sudo swapon /swapfile +sudo apt-get update -y -qq +sudo apt-get install -y -qq --no-install-recommends curl ca-certificates gnupg python3 wget +if [ ! -x "$PG18BIN/initdb" ]; then + sudo install -d /usr/share/postgresql-common/pgdg + sudo curl -fsSL -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc + echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt $(. /etc/os-release; echo $VERSION_CODENAME)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list >/dev/null + sudo apt-get update -y -qq + sudo apt-get install -y -qq --no-install-recommends postgresql-client-18 postgresql-18 + sudo systemctl disable --now postgresql >/dev/null 2>&1 || true # only initdb/psql are used fi - -# --- configuration --------------------------------------------------------- -# The machine-derived formula below is copied from this repo's -# postgresql/install so this entry is configured the same way the other -# PostgreSQL-family entries are, with TWO deviations, in the open (both -# documented in README.md): -# -# work_mem = MemTotal/32 (1 GB on a 32 GB machine) instead of the -# postgresql entry's fixed 64MB. pgrust's grouped-aggregation execution -# keeps per-query hash state in work_mem; at 64MB the large GROUP BY -# queries fall off the in-memory path into partitioned spills and run -# ~10-40x slower. -# -# shared_buffers = MemTotal/8 instead of MemTotal/4. pgrust's columnar -# scans read through their own arenas and the OS page cache rather than -# the buffer pool; a 25% buffer pool is dead weight that starves the -# concurrent-QPS phase (10 connections x ~2 GB of transient aggregation -# state) into the OOM killer. +mkdir -p "$PGRUST_HOME" "$PGSOCKDIR" +rm -rf "$PGRUST_PREFIX"; mkdir -p "$PGRUST_PREFIX" +if [ -n "$PGRUST_TARBALL" ]; then + tar -xzf "$PGRUST_TARBALL" --strip-components=1 -C "$PGRUST_PREFIX" +else + curl -fsSL --retry 5 --retry-delay 5 -o "$PGRUST_HOME/release.tar.gz" "$PGRUST_BIN_URL" + curl -fsSL --retry 5 --retry-delay 5 -o "$PGRUST_HOME/release.tar.gz.sha256" "$PGRUST_BIN_URL.sha256" + (cd "$PGRUST_HOME" && echo "$(cut -c1-64 release.tar.gz.sha256) release.tar.gz" | sha256sum -c -) >/dev/null + tar -xzf "$PGRUST_HOME/release.tar.gz" --strip-components=1 -C "$PGRUST_PREFIX" +fi +"$PGRUST_PREFIX/bin/postgres" --version +rm -rf "$PGDATA" +"$PG18BIN/initdb" -D "$PGDATA" --no-locale --encoding=UTF8 -U postgres -A trust >/dev/null +# Configuration (README.md) memory=$(awk '/MemTotal/ {print $2}' /proc/meminfo) threads=$(nproc) -cpus=$(($threads / 2)) -shared_buffers=$(($memory / 8)) -effective_cache_size=$(($memory - ($memory / 4))) -max_worker_processes=$(($threads + 15)) -work_mem=$(($memory / 32)) - -sudo -u postgres tee -a "$PGDATA_DIR/postgresql.conf" >/dev/null <> "$PGDATA/postgresql.conf" <&2; exit 1; } -chmod 644 "$HITS_FILE" - -# --- load-session server posture ------------------------------------------- -# The COPY below is a single statement over the single as-published file -# (no splitting), but the server executes it with pgrust's parallel COPY -# path, opted in via environment for the load session only: -# -# PGRUST_COPY_PRESORT declares the table's clustered primary-key order — -# the same (CounterID, EventDate, UserID, EventTime, WatchID) key every -# ordered entry in this benchmark uses — and the COPY sorts the rows into -# that order inside the server as it ingests. PGRUST_PARQUET_* enable the -# parallel parquet decoder. The rest of the variables size/enable the -# parallel ingest itself (worker counts, sort memory, in-server lz4 for -# spill runs, ANALYZE sampling parallelism). -# -# These affect the (measured) load phase only: the server is restarted -# WITHOUT any pgrust-specific environment before the query sweep, so the -# scored queries run a stock server. See README.md. -LOAD_ENV="PGRUST_RUNTIME=1 \ -PGRUST_COPY_PRESORT=counterid,eventdate,userid,eventtime,watchid \ -PGRUST_PARALLEL_COPY=1 PGRUST_PARALLEL_COPY_SORT=1 \ -PGRUST_PARALLEL_COPY_DOP=16 PGRUST_PARALLEL_COPY_STITCH_POOL=8 \ -PGRUST_PARALLEL_COPY_FILL_V2=1 PGRUST_PARALLEL_COPY_FILL_PREFETCH=6 \ -PGRUST_PARALLEL_COPY_SORT_MEM=512 \ -PGRUST_PARALLEL_COPY_SORT_MEMRUNS=${PGRUST_LOAD_MEMRUNS_MB:-15360} \ -PGRUST_COPY_RUNLZ4=1 PGRUST_CBSTORE_FASTHASH=1 PGRUST_CBSTORE_DICT_FRAMES=1 \ -PGRUST_PARQUET_PARALLEL=1 PGRUST_PARQUET_BUDGET_MB=4096 \ -PGRUST_ANALYZE_SAMPLE_POOL=12" - -# Restart the server with the load-session environment. Both restarts are -# inside the timed load window. -"$D/stop" -EXTRA_SERVER_ENV="$LOAD_ENV" "$D/start" -for i in $(seq 1 300); do "$D/check" >/dev/null 2>&1 && break; sleep 1; done -"$D/check" >/dev/null - -# Same flow as postgresql/load: fresh DB, DDL, TRUNCATE + COPY FREEZE in one -# transaction (FREEZE requires the truncate in the same transaction), then -# VACUUM ANALYZE. -$PSQL -t -c "DROP DATABASE IF EXISTS test" -$PSQL -t -c "CREATE DATABASE test" -$PSQL -v ON_ERROR_STOP=1 test -t < "$D/create.sql" -# FORMAT 'parquet' and COERCE_EPOCH are pgrust COPY extensions: the server -# decodes the parquet file directly, coercing the file's epoch-encoded -# time columns into the standard TIMESTAMP/DATE schema (the published -# hits.parquet carries no logical types; this is the same conversion the -# duckdb entry does with epoch_ms()/make_date()). -$PSQL -v ON_ERROR_STOP=1 test </dev/null 2>&1 && break; sleep 1; done -"$D/check" >/dev/null -sync +. "$(dirname "$0")/env.sh" +$PSQL -tAc "DROP DATABASE IF EXISTS test" >/dev/null +$PSQL -tAc "CREATE DATABASE test" >/dev/null +$PSQL -v ON_ERROR_STOP=1 -d test -q -f "$(dirname "$0")/create.sql" +$PSQL -v ON_ERROR_STOP=1 -d test -q -c "COPY hits FROM '$HITS_PARQUET' WITH (FORMAT 'parquet', COERCE_EPOCH true)" +$PSQL -v ON_ERROR_STOP=1 -d test -q -c "ANALYZE hits" +got=$($PSQL -d test -tAc 'SELECT count(*) FROM hits') +[ "$got" = 99997497 ] || { echo "load: hits has $got rows, expected 99997497" >&2; exit 1; } diff --git a/pgrust/query b/pgrust/query index 8197470a38..9417d8e0d0 100755 --- a/pgrust/query +++ b/pgrust/query @@ -1,27 +1,13 @@ #!/bin/bash -# Reads a SQL query from stdin, runs it via psql against the `test` DB. -# Stdout: query result. Stderr: runtime in fractional seconds on the last -# line. Exit non-zero on error. Same shape as postgresql/query. +# query: one statement from stdin through psql \timing; seconds to stderr set -e . "$(dirname "$0")/env.sh" - query=$(cat) - -out=$(printf '\\timing\n%s\n' "$query" | $PSQL test -t 2>&1) +out=$(printf '\\timing\n%s\n' "$query" | $PSQL -d test -t 2>&1) status=$? - -if printf '%s\n' "$out" | grep -q '^ERROR\|psql: error'; then - printf '%s\n' "$out" >&2 - exit 1 -fi - +if printf '%s\n' "$out" | grep -q '^ERROR\|psql: error'; then printf '%s\n' "$out" >&2; exit 1; fi printf '%s\n' "$out" | grep -v '^Time:' - time_ms=$(printf '%s\n' "$out" | grep -oP 'Time:\s+\K[0-9]+\.[0-9]+' | tail -n1) -if [ -z "$time_ms" ]; then - echo "no timing in psql output" >&2 - exit 1 -fi +[ -n "$time_ms" ] || { echo "no timing in psql output" >&2; exit 1; } awk -v ms="$time_ms" 'BEGIN { printf "%.3f\n", ms / 1000 }' >&2 - exit "$status" diff --git a/pgrust/results/20260919/c6a.4xlarge.json b/pgrust/results/20260919/c6a.4xlarge.json new file mode 100644 index 0000000000..f29f2bb1e1 --- /dev/null +++ b/pgrust/results/20260919/c6a.4xlarge.json @@ -0,0 +1,237 @@ +{ + "system": "pgrust", + "version": "v0.4-preview", + "proprietary": "no", + "hardware": "cpu", + "tuned": "no", + "tags": [ + "Rust", + "PostgreSQL compatible", + "column-oriented" + ], + "date": "2026-09-19", + "machine": "c6a.4xlarge", + "cluster_size": 1, + "comment": "", + "load_time": 282.123, + "data_size": 9199140796, + "result": [ + [ + 0.003, + 0.001, + 0.001 + ], + [ + 0.003, + 0.001, + 0.001 + ], + [ + 0.003, + 0.001, + 0.001 + ], + [ + 0.003, + 0.001, + 0.001 + ], + [ + 0.278, + 0.068, + 0.07 + ], + [ + 0.329, + 0.028, + 0.029 + ], + [ + 0.003, + 0.001, + 0.001 + ], + [ + 0.034, + 0.004, + 0.005 + ], + [ + 0.377, + 0.105, + 0.11 + ], + [ + 0.581, + 0.196, + 0.203 + ], + [ + 0.223, + 0.022, + 0.025 + ], + [ + 0.251, + 0.027, + 0.029 + ], + [ + 0.357, + 0.044, + 0.045 + ], + [ + 0.616, + 0.163, + 0.167 + ], + [ + 0.411, + 0.071, + 0.072 + ], + [ + 0.258, + 0.073, + 0.074 + ], + [ + 0.683, + 0.187, + 0.188 + ], + [ + 0.074, + 0.003, + 0.004 + ], + [ + 2.518, + 0.568, + 0.565 + ], + [ + 0.106, + 0.005, + 0.005 + ], + [ + 3.27, + 0.023, + 0.023 + ], + [ + 4.016, + 0.016, + 0.017 + ], + [ + 7.19, + 0.035, + 0.036 + ], + [ + 0.727, + 0.013, + 0.013 + ], + [ + 0.061, + 0.012, + 0.007 + ], + [ + 0.152, + 0.004, + 0.004 + ], + [ + 0.065, + 0.006, + 0.007 + ], + [ + 0.8, + 0.132, + 0.134 + ], + [ + 3.256, + 0.506, + 0.517 + ], + [ + 0.006, + 0.002, + 0.002 + ], + [ + 0.408, + 0.092, + 0.091 + ], + [ + 2.819, + 0.078, + 0.079 + ], + [ + 2.617, + 0.218, + 0.212 + ], + [ + 3.254, + 0.132, + 0.132 + ], + [ + 3.258, + 0.131, + 0.131 + ], + [ + 0.201, + 0.087, + 0.089 + ], + [ + 0.074, + 0.007, + 0.008 + ], + [ + 0.02, + 0.004, + 0.004 + ], + [ + 0.066, + 0.004, + 0.004 + ], + [ + 0.118, + 0.024, + 0.029 + ], + [ + 0.033, + 0.003, + 0.004 + ], + [ + 0.037, + 0.005, + 0.006 + ], + [ + 0.028, + 0.005, + 0.007 + ] + ], + "concurrent_qps": 8.85, + "concurrent_error_ratio": 0.061 +} diff --git a/pgrust/results/20260919/c8g.4xlarge.json b/pgrust/results/20260919/c8g.4xlarge.json new file mode 100644 index 0000000000..ab1ec52d0e --- /dev/null +++ b/pgrust/results/20260919/c8g.4xlarge.json @@ -0,0 +1,237 @@ +{ + "system": "pgrust", + "version": "v0.4-preview", + "proprietary": "no", + "hardware": "cpu", + "tuned": "no", + "tags": [ + "Rust", + "PostgreSQL compatible", + "column-oriented" + ], + "date": "2026-09-19", + "machine": "c8g.4xlarge", + "cluster_size": 1, + "comment": "", + "load_time": 159.197, + "data_size": 9199140796, + "result": [ + [ + 0.002, + 0.001, + 0.001 + ], + [ + 0.002, + 0.001, + 0.001 + ], + [ + 0.002, + 0.001, + 0.001 + ], + [ + 0.002, + 0.001, + 0.001 + ], + [ + 0.145, + 0.031, + 0.031 + ], + [ + 0.181, + 0.011, + 0.012 + ], + [ + 0.002, + 0.001, + 0.001 + ], + [ + 0.025, + 0.003, + 0.003 + ], + [ + 0.205, + 0.041, + 0.042 + ], + [ + 0.291, + 0.062, + 0.064 + ], + [ + 0.147, + 0.012, + 0.012 + ], + [ + 0.175, + 0.014, + 0.014 + ], + [ + 0.219, + 0.017, + 0.017 + ], + [ + 0.404, + 0.05, + 0.05 + ], + [ + 0.257, + 0.027, + 0.027 + ], + [ + 0.144, + 0.036, + 0.035 + ], + [ + 0.398, + 0.066, + 0.065 + ], + [ + 0.057, + 0.002, + 0.022 + ], + [ + 2.302, + 0.198, + 0.196 + ], + [ + 0.069, + 0.002, + 0.002 + ], + [ + 3.105, + 0.007, + 0.007 + ], + [ + 3.817, + 0.009, + 0.009 + ], + [ + 7.094, + 0.015, + 0.015 + ], + [ + 0.743, + 0.007, + 0.008 + ], + [ + 0.052, + 0.01, + 0.002 + ], + [ + 0.116, + 0.004, + 0.003 + ], + [ + 0.056, + 0.006, + 0.005 + ], + [ + 0.41, + 0.052, + 0.051 + ], + [ + 3.214, + 0.218, + 0.22 + ], + [ + 0.005, + 0.002, + 0.002 + ], + [ + 0.266, + 0.029, + 0.028 + ], + [ + 2.814, + 0.032, + 0.032 + ], + [ + 2.609, + 0.075, + 0.074 + ], + [ + 3.096, + 0.044, + 0.044 + ], + [ + 3.111, + 0.045, + 0.044 + ], + [ + 0.112, + 0.039, + 0.04 + ], + [ + 0.053, + 0.004, + 0.004 + ], + [ + 0.016, + 0.003, + 0.003 + ], + [ + 0.053, + 0.003, + 0.003 + ], + [ + 0.102, + 0.014, + 0.013 + ], + [ + 0.026, + 0.002, + 0.003 + ], + [ + 0.026, + 0.003, + 0.004 + ], + [ + 0.02, + 0.003, + 0.003 + ] + ], + "concurrent_qps": 30.692, + "concurrent_error_ratio": 0.034 +} diff --git a/pgrust/start b/pgrust/start index 8251ccf250..694daa3aed 100755 --- a/pgrust/start +++ b/pgrust/start @@ -1,24 +1,11 @@ #!/bin/bash set -eu . "$(dirname "$0")/env.sh" - -sudo install -d -o postgres -g postgres "$PGSOCK_DIR" - -# Runs as the `postgres` system user (the server refuses to run as root, -# and the datadir is owned by postgres). RUST_MIN_STACK and the rlimit -# raise thread/main stacks to match max_stack_depth=60000 — pgrust -# requires both (deep recursive expression evaluation paths). -sudo -u postgres \ - PGRUST_BIN="$PGRUST_BIN" PGDATA_DIR="$PGDATA_DIR" \ - PGSOCK_DIR="$PGSOCK_DIR" SERVER_LOG="$SERVER_LOG" \ - PGSHARE_DIR="$PGSHARE_DIR" TZDATA_DIR="$TZDATA_DIR" \ - EXTRA_SERVER_ENV="${EXTRA_SERVER_ENV:-}" \ - bash -c ' - ulimit -s 65520 - nohup env RUST_MIN_STACK=33554432 RUST_BACKTRACE=1 \ - PGRUST_TZDIR="$TZDATA_DIR" PGRUST_PGSHAREDIR="$PGSHARE_DIR" \ - $EXTRA_SERVER_ENV \ - "$PGRUST_BIN" -D "$PGDATA_DIR" -k "$PGSOCK_DIR" -p 5432 \ - -c listen_addresses="" \ - >> "$SERVER_LOG" 2>&1 & - ' +$PSQL -tAc 'SELECT 1' >/dev/null 2>&1 && exit 0 +mkdir -p "$PGSOCKDIR" +ulimit -s 65520 2>/dev/null || true # max_stack_depth=60000 needs the stack limit raised +nohup env RUST_MIN_STACK=67108864 \ + PGRUST_TZDIR=/usr/share/zoneinfo PGRUST_PGSHAREDIR="$PGRUST_PREFIX/share/postgresql" \ + PGRUST_PARALLEL_COPY=1 PGRUST_PARQUET_PARALLEL=1 \ + "$PGRUST_PREFIX/bin/postgres" -D "$PGDATA" -p "$PGPORT" >> "$PGRUST_HOME/server.log" 2>&1 < /dev/null & +exit 0 diff --git a/pgrust/stop b/pgrust/stop index 943bc16e5c..fadad26dda 100755 --- a/pgrust/stop +++ b/pgrust/stop @@ -1,23 +1,6 @@ #!/bin/bash . "$(dirname "$0")/env.sh" - -pid=$(sudo head -1 "$PGDATA_DIR/postmaster.pid" 2>/dev/null) -[ -n "$pid" ] || exit 0 - -# Fast shutdown, as the true-cold-run protocol requires. -sudo kill -INT "$pid" 2>/dev/null || exit 0 -for i in $(seq 1 120); do - sudo kill -0 "$pid" 2>/dev/null || exit 0 - sleep 0.5 -done - -# Still alive after 60s: immediate shutdown. The datadir is durable and the -# next start recovers; better than leaving a half-stopped server pinning the -# page cache through the "cold" cycle. -echo "pgrust/stop: fast shutdown did not finish in 60s, escalating to SIGQUIT" >&2 -sudo kill -QUIT "$pid" 2>/dev/null || exit 0 -for i in $(seq 1 60); do - sudo kill -0 "$pid" 2>/dev/null || exit 0 - sleep 0.5 -done +PIDF="$PGDATA/postmaster.pid" +[ -f "$PIDF" ] && kill -INT "$(head -1 "$PIDF")" 2>/dev/null +for i in $(seq 1 120); do [ -f "$PIDF" ] && kill -0 "$(head -1 "$PIDF")" 2>/dev/null || exit 0; sleep 0.5; done exit 0 diff --git a/pgrust/template.json b/pgrust/template.json index 2fdd9c9dd9..b72bbde84f 100644 --- a/pgrust/template.json +++ b/pgrust/template.json @@ -1,11 +1,8 @@ { "system": "pgrust", + "version": "v0.4-preview", "proprietary": "no", "hardware": "cpu", "tuned": "no", - "tags": [ - "Rust", - "column-oriented", - "PostgreSQL compatible" - ] + "tags": ["Rust", "PostgreSQL compatible", "column-oriented"] }