From 993890f0e1647178e4612827bf234cc6b241c594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Fern=C3=A1ndez?= Date: Wed, 26 Apr 2023 19:38:39 +0200 Subject: [PATCH] perf(qe): Examples useful for performance analysis of connecting to DB servers (#3892) * Update benchmarking/profiling scripts * Add a few useful examples for benchmarking / profiling * Update Dockerfile to profile code snippets in typescript too --- Cargo.lock | 82 +++++++++++++++++-- Makefile | 9 +- .../connectors/sql-query-connector/Cargo.toml | 5 ++ .../examples/mysql_async.rs | 21 +++++ .../examples/quaint_query.rs | 26 ++++++ .../sql-query-connector/examples/tokio_pg.rs | 25 ++++++ script/.profile-shell/Dockerfile | 33 ++++++++ script/.profile-shell/bin/build | 4 + script/.profile/Dockerfile | 26 ------ script/.profile/bin/build | 3 - script/.profile/bin/build-and-run | 3 - script/.profile/bin/run-valgrind | 23 ------ script/README.md | 70 ++++++++-------- script/profile | 7 -- script/profile-shell | 12 +-- 15 files changed, 234 insertions(+), 115 deletions(-) create mode 100644 query-engine/connectors/sql-query-connector/examples/mysql_async.rs create mode 100644 query-engine/connectors/sql-query-connector/examples/quaint_query.rs create mode 100644 query-engine/connectors/sql-query-connector/examples/tokio_pg.rs create mode 100644 script/.profile-shell/Dockerfile create mode 100755 script/.profile-shell/bin/build delete mode 100644 script/.profile/Dockerfile delete mode 100755 script/.profile/bin/build delete mode 100755 script/.profile/bin/build-and-run delete mode 100755 script/.profile/bin/run-valgrind delete mode 100755 script/profile diff --git a/Cargo.lock b/Cargo.lock index a18c120cecfb..d0bf640c2283 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2861,6 +2861,20 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "postgres" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960c214283ef8f0027974c03e9014517ced5db12f021a9abb66185a5751fab0a" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "postgres-native-tls" version = "0.5.0" @@ -2869,7 +2883,7 @@ dependencies = [ "native-tls", "tokio", "tokio-native-tls", - "tokio-postgres", + "tokio-postgres 0.7.7 (git+https://github.com/prisma/rust-postgres?branch=pgbouncer-mode)", ] [[package]] @@ -2889,6 +2903,24 @@ dependencies = [ "stringprep", ] +[[package]] +name = "postgres-protocol" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d" +dependencies = [ + "base64 0.21.0", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand 0.8.5", + "sha2 0.10.5", + "stringprep", +] + [[package]] name = "postgres-types" version = "0.2.4" @@ -2898,12 +2930,23 @@ dependencies = [ "bytes", "chrono", "fallible-iterator", - "postgres-protocol", + "postgres-protocol 0.6.4", "serde", "serde_json", "uuid 1.1.2", ] +[[package]] +name = "postgres-types" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f028f05971fe20f512bcc679e2c10227e57809a3af86a7606304435bc8896cd6" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol 0.6.5", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -3163,14 +3206,14 @@ dependencies = [ "num_cpus", "percent-encoding", "postgres-native-tls", - "postgres-types", + "postgres-types 0.2.4", "rusqlite", "serde_json", "sqlformat", "thiserror", "tiberius", "tokio", - "tokio-postgres", + "tokio-postgres 0.7.7 (git+https://github.com/prisma/rust-postgres?branch=pgbouncer-mode)", "tokio-util 0.6.10", "tracing", "tracing-core", @@ -4272,8 +4315,10 @@ dependencies = [ "cuid", "futures", "itertools", + "mysql_async", "once_cell", "opentelemetry", + "postgres", "prisma-models", "prisma-value", "psl", @@ -4284,6 +4329,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tokio-postgres 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "tracing-futures", "tracing-opentelemetry", @@ -4726,6 +4772,30 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a12c1b3e0704ae7dfc25562629798b29c72e6b1d0a681b6f29ab4ae5e7f7bf" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot 0.12.1", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol 0.6.5", + "postgres-types 0.2.5", + "socket2", + "tokio", + "tokio-util 0.7.3", +] + [[package]] name = "tokio-postgres" version = "0.7.7" @@ -4742,8 +4812,8 @@ dependencies = [ "percent-encoding", "phf", "pin-project-lite", - "postgres-protocol", - "postgres-types", + "postgres-protocol 0.6.4", + "postgres-types 0.2.4", "socket2", "tokio", "tokio-util 0.7.3", diff --git a/Makefile b/Makefile index 6975ed5f1020..7ebd07f65935 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,16 @@ LIBRARY_EXT := $(shell \ default: build -##################### -# Boostrap commands # -##################### +################### +# script wrappers # +################### bootstrap-darwin: script/bootstrap-darwin +profile-shell: + script/profile-shell + ################## # Build commands # ################## diff --git a/query-engine/connectors/sql-query-connector/Cargo.toml b/query-engine/connectors/sql-query-connector/Cargo.toml index b2bc76b850f4..94b4a030974e 100644 --- a/query-engine/connectors/sql-query-connector/Cargo.toml +++ b/query-engine/connectors/sql-query-connector/Cargo.toml @@ -49,3 +49,8 @@ version = "1.2" [dependencies.user-facing-errors] features = ["sql"] path = "../../../libs/user-facing-errors" + +[dev-dependencies] +postgres = "0.19" +tokio-postgres = "0.7" +mysql_async = { git="https://github.com/prisma/mysql_async", branch="vendored-openssl" } diff --git a/query-engine/connectors/sql-query-connector/examples/mysql_async.rs b/query-engine/connectors/sql-query-connector/examples/mysql_async.rs new file mode 100644 index 000000000000..9bd5980809b0 --- /dev/null +++ b/query-engine/connectors/sql-query-connector/examples/mysql_async.rs @@ -0,0 +1,21 @@ +use mysql_async::prelude::*; +use std::time::Instant; + +#[tokio::main] +async fn main() -> () { + let url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set to a mysql URL"); + let start = Instant::now(); + + let now = Instant::now(); + let pool = mysql_async::Pool::from_url(&url).unwrap(); + println!("Building pool: {:?}", now.elapsed()); + + let mut conn = pool.get_conn().await.unwrap(); + println!("Checking out connection: {:?}", now.elapsed()); + + let now = Instant::now(); + let _: Vec = conn.query("SELECT 1").await.unwrap(); + println!("Query: {:?}", now.elapsed()); + + println!("Total: {:?}", start.elapsed()); +} diff --git a/query-engine/connectors/sql-query-connector/examples/quaint_query.rs b/query-engine/connectors/sql-query-connector/examples/quaint_query.rs new file mode 100644 index 000000000000..cc8dd36ef3af --- /dev/null +++ b/query-engine/connectors/sql-query-connector/examples/quaint_query.rs @@ -0,0 +1,26 @@ +use std::time::Instant; + +use quaint::{pooled::Quaint, prelude::Queryable}; + +#[tokio::main] +async fn main() -> () { + let url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set to a quaint-compatible database URL"); + let start = Instant::now(); + + let now = Instant::now(); + let mut builder = Quaint::builder(&url).expect("should connect"); + builder.health_check_interval(std::time::Duration::from_secs(15)); + builder.test_on_check_out(true); + let pool = builder.build(); + println!("Building pool: {:?}", now.elapsed()); + + let now = Instant::now(); + let conn = pool.check_out().await.unwrap(); + println!("Checking out connection: {:?}", now.elapsed()); + + let now = Instant::now(); + conn.execute_raw("SELECT 1", &[]).await.unwrap(); + println!("Query: {:?}", now.elapsed()); + + println!("Total: {:?}", start.elapsed()); +} diff --git a/query-engine/connectors/sql-query-connector/examples/tokio_pg.rs b/query-engine/connectors/sql-query-connector/examples/tokio_pg.rs new file mode 100644 index 000000000000..8986af7e74d3 --- /dev/null +++ b/query-engine/connectors/sql-query-connector/examples/tokio_pg.rs @@ -0,0 +1,25 @@ +use std::time::Instant; + +#[tokio::main] +async fn main() -> () { + let url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set to a postgres URL"); + let start = Instant::now(); + + let now = Instant::now(); + let (client, connection) = tokio_postgres::connect(&url, postgres::NoTls).await.unwrap(); + println!("Connect: {:?}", now.elapsed()); + + tokio::spawn(async move { + let now = Instant::now(); + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + eprintln!("connection time: {:?}", now.elapsed()); + }); + + let now = Instant::now(); + client.query("SELECT 1;", &[]).await.unwrap(); + println!("Query: {:?}", now.elapsed()); + + println!("Total: {:?}", start.elapsed()); +} diff --git a/script/.profile-shell/Dockerfile b/script/.profile-shell/Dockerfile new file mode 100644 index 000000000000..4b2419ac0711 --- /dev/null +++ b/script/.profile-shell/Dockerfile @@ -0,0 +1,33 @@ +ARG RUST_VERSION +FROM rust:$RUST_VERSION-buster +RUN printf "deb http://ftp.de.debian.org/debian buster main" >> /etc/apt/sources.list +RUN apt update +RUN apt-get install -y git valgrind linux-perf strace + +RUN curl -fsSL https://fnm.vercel.app/install | bash +RUN source ~/.bashrc +RUN fnm install v16 + +# Symlink caches +RUN mkdir -p /.cargo/registry +RUN mkdir .cargo/git +RUN rm -rf /usr/local/cargo/git \ + /usr/local/cargo/registry \ + /usr/local/cargo/.package-cache \ + /usr/local/cargo/.crates.toml \ + /usr/local/cargo/.crates2.json +RUN ln -s /.cargo/git /usr/local/cargo +RUN ln -s /.cargo/registry /usr/local/cargo +RUN ln -s /.cargo/.package-cache /usr/local/cargo +RUN ln -s /.cargo/.crates.toml /usr/local/cargo +RUN ln -s /.cargo/.crates2.json /usr/local/cargo + +# Set env +ENV PATH="${PATH}:/prisma-engines/script/.profile-shell/bin" +ENV VALGRIND_LIB=/usr/lib/aarch64-linux-gnu/valgrind/ +ENV CARGO_TARGET_DIR=target-alternatives + +# Install cargo-flamegraph +RUN cargo install flamegraph + +CMD ["/bin/bash"] \ No newline at end of file diff --git a/script/.profile-shell/bin/build b/script/.profile-shell/bin/build new file mode 100755 index 000000000000..46624702c382 --- /dev/null +++ b/script/.profile-shell/bin/build @@ -0,0 +1,4 @@ +#/bin/bash +CMD="cargo build --profile=profiling --examples --benches" +echo "==> Running command: $CMD" +cd /prisma-engines && eval "$CMD" \ No newline at end of file diff --git a/script/.profile/Dockerfile b/script/.profile/Dockerfile deleted file mode 100644 index d060a75d2447..000000000000 --- a/script/.profile/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -ARG RUST_VERSION -FROM rust:$RUST_VERSION-buster -RUN printf "deb http://ftp.de.debian.org/debian buster main" >> /etc/apt/sources.list -RUN apt update -RUN apt-get install -y git valgrind - -# Symlink caches -RUN mkdir -p /.cargo/registry \ - /.cargo/git \ - /.cargo/.package-cache -RUN rm -rf /usr/local/cargo/git \ - /usr/local/cargo/registry \ - /usr/local/cargo/.package-cache \ - /usr/local/cargo/.crates.toml \ - /usr/local/cargo/.crates2.json -RUN ln -s /.cargo/git /usr/local/cargo -RUN ln -s /.cargo/registry /usr/local/cargo -RUN ln -s /.cargo/.package-cache /usr/local/cargo -RUN ln -s /.cargo/.crates.toml /usr/local/cargo -RUN ln -s /.cargo/.crates2.json /usr/local/cargo - -# Set env -ENV PATH="${PATH}:/rustrepo/script/.profile/bin" -ENV VALGRIND_LIB=/usr/lib/aarch64-linux-gnu/valgrind/ -ENV VALID_TOOLS="memcheck cachegrind callgrind helgrind drd massif sgcheck memcheck-dhat" -ENV CARGO_TARGET_DIR=target-alternatives \ No newline at end of file diff --git a/script/.profile/bin/build b/script/.profile/bin/build deleted file mode 100755 index fc1e8c056906..000000000000 --- a/script/.profile/bin/build +++ /dev/null @@ -1,3 +0,0 @@ -#/bin/bash -echo "==> Building examples to profile..." -cd /rustrepo && cargo build --profile=profiling --examples \ No newline at end of file diff --git a/script/.profile/bin/build-and-run b/script/.profile/bin/build-and-run deleted file mode 100755 index 34ff1df51b43..000000000000 --- a/script/.profile/bin/build-and-run +++ /dev/null @@ -1,3 +0,0 @@ -#/bin/bash -echo "==> Running: build && run-valgrind $@" -build && run-valgrind $@ \ No newline at end of file diff --git a/script/.profile/bin/run-valgrind b/script/.profile/bin/run-valgrind deleted file mode 100755 index 63ddd55d023a..000000000000 --- a/script/.profile/bin/run-valgrind +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -VALID_TOOLS=" memcheck cachegrind callgrind helgrind drd massif sgcheck dhat " -EXAMPLES_DIR="/rustrepo/target-alternatives/profiling/examples" -VALID_EXAMPLES=$(find $EXAMPLES_DIR -type f -perm -u=x -printf "%f\n") -DEFAULT_EXAMPLE=$(echo "${VALID_EXAMPLES}" | head -n1) -EXAMPLE=${2:-${DEFAULT_EXAMPLE}} -TOOL=$1 -ARGS=${*:3} - -if [ -z "$2" ] || ! [[ " ${VALID_TOOLS} " == *" $TOOL "* ]] || [ ! -f "$EXAMPLES_DIR/$EXAMPLE" ]; then - echo "" - echo "❌ Failed to run command: $*" - echo "" - echo " Usage: $0 tool example" - echo " Valid tools: ${VALID_TOOLS}. See https://valgrind.org/docs/manual/manual.html" - echo " Valid examples: ${VALID_EXAMPLES}" - echo " Example: $0 massif ${DEFAULT_EXAMPLE}" - exit 1 -else - CMD="valgrind $ARGS --tool=$TOOL $EXAMPLES_DIR/$EXAMPLE" - echo "==> Running valgrind: $CMD" - exec $CMD -fi diff --git a/script/README.md b/script/README.md index aba4c0109c5e..ac7bc3f60b2d 100644 --- a/script/README.md +++ b/script/README.md @@ -4,8 +4,6 @@ Inspired by https://github.com/github/scripts-to-rule-them-all > If your scripts are normalized by name across all of your projects, your contributors only need to know the pattern, not a deep knowledge of the application. This means they can jump into a project and make contributions without first learning how to bootstrap the project or how to get its tests to run. -This scripts can also be run with make. Following the convention: `script/$TARGET` -> `make $TARGET`. For instance, make bootstrap-darwin will run `script/bootstrap-darwin` - If instead you are a user of the [nix](https://nixos.org/manual/nix/stable/) package manager, you might find interesting packages and in the [nix](../nix/) directory. ### The scripts @@ -16,46 +14,46 @@ If instead you are a user of the [nix](https://nixos.org/manual/nix/stable/) pac The goal is to make sure all required dependencies are installed, and the workspace builds. -#### script/profile +#### script/profile-shell -[`script/profile`](profile) allows to run Valgrind profiling over any example in the primsa-engines -repository, using a linux docker image as a bride. +[`script/profile-shell`](profile-shell) allows to run Valgrind profiling over any example in the primsa-engines repository, using a linux docker image as a bridge. -Example. Let´s say we want to use [`massif`](https://valgrind.org/docs/manual/ms-manual.html) to profile heap allocations, same will apply to other tools: +For example, say we want to use [`massif`](https://valgrind.org/docs/manual/ms-manual.html) to valgrind heap allocations: + +First, we can build the latest version of the repository in linux. -```sh -> ~/GitHub/prisma/prisma-engines -❯ script/profile massif schema_builder_build_odoo -==> Profiling docker image (prisma-engines-profile:1.68.0.31f76) exists, using it -==> Running command: docker run -it -v /Users/miguel/GitHub/prisma/prisma-engines:/rustrepo -v /Users/miguel/.cargo:/.cargo -w /rustrepo prisma-engines-profile:1.68.0.31f76 bash -C build-and-run massif schema_builder_build_odoo -==> Running: build && run-valgrind massif schema_builder_build_odoo -==> Building examples to profile... - Finished profiling [optimized + debuginfo] target(s) in 15.08s -warning: the following packages contain code that will be rejected by a future version of Rust: connection-string v0.1.13 -note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 33` -==11== Massif, a heap profiler -==11== Copyright (C) 2003-2017, and GNU GPL'd, by Nicholas Nethercote -==11== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info -==11== Command: /rustrepo/target-alternatives/profiling/examples/schema_builder_build_odoo -==11== -Elapsed: 5.78s -==11== ``` +❯ script/profile-shell +==> Running command: docker build -t prisma-engines-profile:latest --build-arg RUST_VERSION=1.68.0 -f script/.profile-shell/Dockerfile . +[+] Building 1.0s (16/16) FINISHED +==> Running command: docker run -it --rm -v /Users/miguel/GitHub/prisma/prisma-engines:/prisma-engines -v /Users/miguel/.cargo:/.cargo -w /prisma-engines prisma-engines-profile:latest -The massif output file was generated in the own repository folder, which was mounted in the docker image +# Now we build the prisma-engines, build artifacts will get cached +# in the /prisma-engines/target-alternatives/profiling directory -```sh -❯ git status -Untracked files: - massif.out.11 -``` +root@42b8f52761fb:/prisma-engines# build +==> Running command: cargo build --profile=profiling --examples --benches + Finished profiling [optimized + debuginfo] target(s) in 25.89s +warning: the following packages contain code that will be rejected by a future version of Rust: connection-string v0.1.13 +note: to see what the problems were, use the option -Then use [`script/profile-shell`](profile-shell) to view the results using `ms-print` as the [docs](https://valgrind.org/docs/manual/ms-manual.html) suggest: +# Once built, we can profile one of the examples +# in the /prisma-engines/target-alternatives/profiling directory -``` -```sh -❯ script/profile-shell ms_print /rustrepo/massif.out.11 |tail -n10 +root@42b8f52761fb:/prisma-engines# valgrind --tool=massif target-alternatives/profiling/examples/schema_builder_build_odoo +==12== Massif, a heap profiler +==12== Copyright (C) 2003-2017, and GNU GPL'd, by Nicholas Nethercote +==12== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info +==12== Command: target-alternatives/profiling/examples/schema_builder_build_odoo +==12== +Elapsed: 3.82s +==12== + +# And visualize the profiliong results using any tool provided by the +# valgrant package + +root@42b8f52761fb:/prisma-engines# ms_print /prisma-engines/massif.out.11 |tail -n10 ->01.42% (2,305,284B) 0x151446: ::clone (alloc.rs:95) ->01.42% (2,305,284B) in 42 places, all below massif's threshold (1.00%) @@ -66,8 +64,4 @@ Then use [`script/profile-shell`](profile-shell) to view the results using `ms-p 52 2,423,804,986 94,167,552 83,189,217 10,978,335 0 53 2,447,524,833 47,307,048 41,851,522 5,455,526 0 54 2,471,244,705 4,387,240 4,122,973 264,267 0 - ``` - -#### script/profile-shell - -[`script/profile-shell`](profile-shell). Use it to run arbitrary commands on the docker profiling image. See the example provided in [`script/profile`](profile) \ No newline at end of file + ``` \ No newline at end of file diff --git a/script/profile b/script/profile deleted file mode 100755 index 9fa57d619402..000000000000 --- a/script/profile +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# script/profile: Profile any example in the repository using valgrind tools like DHAT and Massif. - -set -e -cd "$(dirname "$0")" -./profile-shell "bash -C build-and-run $*" diff --git a/script/profile-shell b/script/profile-shell index eb118d142f60..8fabf9e4d2a2 100755 --- a/script/profile-shell +++ b/script/profile-shell @@ -7,9 +7,9 @@ set -e cd "$(dirname "$0")/.." rust_version=$(rustc --version | cut -d ' ' -f 2) -CMD="docker build -t prisma-engines-profile:latest --build-arg RUST_VERSION=$rust_version -f script/.profile/Dockerfile ." -echo "==> Running command: $CMD" -eval "$CMD" -CMD="docker run -it -v $PWD:/rustrepo -v $HOME/.cargo:/.cargo -w /rustrepo prisma-engines-profile:latest $*" -echo "==> Running command: $CMD" -exec $CMD +DOCKER_CMD="docker build -t prisma-engines-profile:latest --build-arg RUST_VERSION=$rust_version -f script/.profile-shell/Dockerfile ." +echo "==> Running command: $DOCKER_CMD" +eval "$DOCKER_CMD" +DOCKER_CMD="docker run -it --rm -v $PWD:/prisma-engines -v $HOME/.cargo:/.cargo -w /prisma-engines prisma-engines-profile:latest" +echo "==> Running command: $DOCKER_CMD" +exec $DOCKER_CMD