Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(postgres): use faster sql.DB instead of docker exec psql for snapshot/restore [rebased for main] #2600

Merged

Conversation

cfstras
Copy link
Contributor

@cfstras cfstras commented Jun 21, 2024

See description of #2599

This is the same PR, but rebased for main.

The original description follows, just with latest benchmarks


What does this PR do?

The postgres module has functions for saving snapshots and restoring them later.
This feature is helpful for integration testing without having to manually clear the database.

This PR speeds up the snapshot/restore functionality by executing the necessary SQL commands via a regular sql.DB connection.

If the driver is not available in the test environment, or the commands fail for some other reason (SSL config, etc), it falls back to the old variant of calling docker exec.

Why is it important?

docker exec can be unnecessarily slow, especially when using docker in a VM with Docker Desktop or colima.

On my laptop (MacBook Air M1), each call to Container.Restore() takes about 150~200ms.
In a small subset of 20 of our tests, where each test restores the database before starting, with creating the initial container, this brings the total time up to 12 seconds, which is quite a lot for a few small database tests.

After these changes, the same subset of tests now takes 6 seconds.

Related issues

How to test this PR

cd modules/postgres
go test ./...

Follow-ups

I've also considered caching the DB pool that is created to manage the databases.
After running some benchmark with this code (from branch X), I noticed it does not have a big impact on the whole test suite - my internal test suite does not change in execution time (since the time is dominated by re-connecting the actual test client, which will be terminated when the database gets deleted during the restore).
The module/postgres test suite even gets slower:

cached: main...cfstras:postgres-snapshot-sql-cached-rebased-main
not cached: this PR

testcontainers main, sql, not cached: 42.227 s ± 0.635
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     42.227 s ±  0.635 s    [User: 1.213 s, System: 1.034 s]
  Range (min … max):   41.832 s … 44.006 s    10 runs
testcontainers main, sql, cached: 43.207 s ± 1.549
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     43.207 s ±  1.549 s    [User: 1.247 s, System: 1.057 s]
  Range (min … max):   41.745 s … 45.664 s    10 runs
testcontainers main, without PR (but amended tests): 45.298 s ± 5.610 s Branch: cfstras:testcontainers-go:main-with-postgres-sql-test-fixes
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     45.298 s ±  5.610 s    [User: 1.218 s, System: 1.005 s]
  Range (min … max):   41.243 s … 57.096 s    10 runs
internal testsuite, with testcontainers main, sql, cached, with ryuk: 14.123 s ± 1.795 s
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     14.123 s ±  1.795 s    [User: 9.589 s, System: 2.749 s]
  Range (min … max):   11.407 s … 16.748 s    10 runs
internal testsuite, with testcontainers main, sql, cached, without ryuk: 11.299 s ± 1.503 s
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     11.299 s ±  1.503 s    [User: 9.818 s, System: 2.728 s]
  Range (min … max):    9.928 s … 13.781 s    10 runs
internal testsuite, with testcontainers main, sql, not cached, without ryuk: 10.597 s ± 0.641 s
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     10.597 s ±  0.641 s    [User: 9.812 s, System: 2.820 s]
  Range (min … max):    9.894 s … 12.029 s    10 runs
internal testsuite, with testcontainers main, without my PR, not cached, without ryuk: 14.245 s ± 0.639 s
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     14.245 s ±  0.639 s    [User: 9.671 s, System: 2.760 s]
  Range (min … max):   13.742 s … 15.980 s    10 runs
internal testsuite, with testcontainers main, docker-exec fallback, not cached, without ryuk: 14.216 s ± 0.450 s
➜ hyperfine "go test ./... -count 1"
Benchmark 1: go test ./... -count 1
  Time (mean ± σ):     14.216 s ±  0.450 s    [User: 9.566 s, System: 2.654 s]
  Range (min … max):   13.735 s … 15.278 s    10 runs

Copy link

netlify bot commented Jun 21, 2024

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 9330f0b
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-go/deploys/66840a28177e55000813692d
😎 Deploy Preview https://deploy-preview-2600--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

No default is supplied, so you need to set it explicitly.

<!--codeinclude-->
[Example Wait Strategies](../../modules/postgres/postgres_test.go) inside_block:waitStrategy
Copy link
Member

@mdelapenya mdelapenya Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the docs path:

Suggested change
[Example Wait Strategies](../../modules/postgres/postgres_test.go) inside_block:waitStrategy
[Example Wait Strategies](../../modules/postgres/wait_strategies.go) inside_block:waitStrategy

Once this is done and the CI passes, I think we can merge this one 🚀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Fixed those two nits, LMK how it goes.

FYI going on holiday in a few hours, so if there's more things you want me to change it'll have to wait until next week.

Thanks so much for engaging with this :)

modules/postgres/postgres.go Outdated Show resolved Hide resolved
@mdelapenya
Copy link
Member

@cfstras @ash2k I went ahead and added d8468ec converting the global variable into a container option. PLMK what you think.

Cheers!

@cfstras
Copy link
Contributor Author

cfstras commented Jul 2, 2024

Looks good to me! :) Thanks for making the change.

@mdelapenya mdelapenya self-assigned this Jul 2, 2024
@mdelapenya mdelapenya added the feature New functionality or new behaviors on the existing one label Jul 2, 2024
Copy link
Member

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@mdelapenya mdelapenya merged commit 0d7ebaf into testcontainers:main Jul 2, 2024
108 checks passed
mdelapenya added a commit that referenced this pull request Jul 4, 2024
* main:
  chore(deps): bump mkdocs-include-markdown-plugin from 6.0.4 to 6.2.1 (#2617)
  chore(deps): bump peter-evans/slash-command-dispatch from 3.0.2 to 4.0.0 (#2561)
  docs: document ryuk timeouts for compose (#2620)
  chore: use self-hosted worker for Windows tests (#2619)
  feat(postgres): use faster sql.DB instead of docker exec psql for snapshot/restore [rebased for main] (#2600)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New functionality or new behaviors on the existing one
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants