Skip to content

Commit 85cc50b

Browse files
authored
Full schema insertion + schema testing (#848)
* Full schema insertion + schema testing Here, introduce a full version of schema injection as initially started in #798, and exposing a new `Schema` option to make it configurable even outside the test suite. This involved fixing a lot of bugs from #798, and the only way to make it possible to root them all out was to make full use of schemas across the entire test suite. The original "test DB manager" system has been replaced with a new `riverschematest.TestSchema` helper that generates a schema for use with a test case or prefers an existing idle one that was already generated for the same test run. `TestSchema` runs migrations for generated schemas which also means we don't need to use `testdbman` anymore, with tests capable of bootstrapping themselves at run time. We reuse schemas to avoid this extra work when possible, but migrating a new schema is also surprisingly fast, taking up to 50ms, but getting more down to a stable ~10ms once things are warmed up. Here's a series of sample timings: $ go test ./rivermigrate -run TestMigrator/MigrateUpDefault -test.v -count 50 | grep 'migrations ran in' river_migrate_test.go:492: migrations ran in 45.571209ms river_migrate_test.go:492: migrations ran in 24.642458ms river_migrate_test.go:492: migrations ran in 16.749708ms river_migrate_test.go:492: migrations ran in 22.970375ms river_migrate_test.go:492: migrations ran in 16.201375ms river_migrate_test.go:492: migrations ran in 15.727625ms river_migrate_test.go:492: migrations ran in 13.291333ms river_migrate_test.go:492: migrations ran in 13.680708ms river_migrate_test.go:492: migrations ran in 14.867416ms river_migrate_test.go:492: migrations ran in 15.631916ms river_migrate_test.go:492: migrations ran in 13.873791ms river_migrate_test.go:492: migrations ran in 14.8645ms river_migrate_test.go:492: migrations ran in 14.92575ms river_migrate_test.go:492: migrations ran in 12.541834ms river_migrate_test.go:492: migrations ran in 14.753875ms river_migrate_test.go:492: migrations ran in 12.694334ms river_migrate_test.go:492: migrations ran in 13.955917ms river_migrate_test.go:492: migrations ran in 12.126458ms river_migrate_test.go:492: migrations ran in 14.095958ms river_migrate_test.go:492: migrations ran in 13.273375ms river_migrate_test.go:492: migrations ran in 13.988917ms river_migrate_test.go:492: migrations ran in 13.141459ms river_migrate_test.go:492: migrations ran in 12.394417ms river_migrate_test.go:492: migrations ran in 11.539208ms river_migrate_test.go:492: migrations ran in 11.577834ms river_migrate_test.go:492: migrations ran in 10.883375ms river_migrate_test.go:492: migrations ran in 10.547417ms river_migrate_test.go:492: migrations ran in 12.330375ms river_migrate_test.go:492: migrations ran in 11.54575ms river_migrate_test.go:492: migrations ran in 11.437458ms river_migrate_test.go:492: migrations ran in 10.957ms river_migrate_test.go:492: migrations ran in 10.589083ms river_migrate_test.go:492: migrations ran in 9.758583ms Removal of the "test DB manager" system also means that we can ungate test from `-p 1` because they're all able to run in parallel now. The limiting factor I ran in is that we need to keep max pool connections within each package's tests to a relatively modest number (I found 15 seemed to maximum success) so parallel packages don't exceed the default Postgres configuration of 100 connections. Something that can be kind of annoying is that in case a schema isn't used properly somewhere in a test case (i.e. `TestSchema` is run, but then not used), inserts/operations will go the default schema, which will leave debris there, and that will interfere with test cases using `TestTx` (with test DB manager, all debris would go to a different database so you wouldn't notice). To remediate this, I've added a cleanup hook to `TestSchema` that looks for leftovers that may have been added to the default schema. This isn't perfect because those leftovers may have come from another test case running in parallel or which ran previously, but it helps to zero in on the original source of the issue. * Use schema testing for `TestTx`, sharing one schema per set of migration lines Here, start using a new isolated schema for test transaction invocations rather than falling back on whatever happens to be in `river_test`. A major benefit of this approach is that it makes migration inconsistencies impossible so that when checking out a branch with a new migration, your tests will always get the new migration, but then drop it when moving back to the `master` branch. It has the additional advantage that when alternatively running River and River Pro tests locally pointing to the same `river_test` database, we get the right migration lines raised for `TestTx` without having to worry about whether Pro migrations have been made available yet or not. We attempt to keep test transactions very efficient by only raising one schema per package being tested (and per set of migration lines, although this is rarely used) so we don't need to create a schema and run migrations once per test, but rather once for _all_ tests in the package. Similar to `TestSchema`, `TestTx` picks up a driver so that it can use `GetMigrationDefaultLines` to determine what migration lines a test case is expecting to exist before running. Once again, this is mainly useful for River Pro. `TestTx` gets moved to the higher level `riverschematest` package because it needs access to high level constructs like migrations, and I rename `riverschematest` to `riverdbtest` because with test transactions in it, its use becomes more general. I would've also moved functions in `riversharedtest` like `DBPool` and `TestDatabaseURL` over, but unfortunately we still need these at a lower level so that the tests in `rivermigrate` can use them. I removed the `riverdbtest` functionality that checks for extraneous rows. The `public` schema no longer even exists in CI, and the check is fairly unnecessary now that zeroing `search_path` makes it very difficult to accidentally insert records into anything but the current test schema. * Inject schema to completer instead of each job being completed Per code review feedback, instead of sending schema into the executor and then through into each completion being made, inject it once into the completer, then have the completer add the schema as it's completing batches. In practice, this ends up removing a lot of schema noise, and the job executor no longer needs to know about schema at all.
1 parent cc5f0e0 commit 85cc50b

118 files changed

Lines changed: 3444 additions & 2884 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yaml

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ env:
88
DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/river_dev?sslmode=disable
99

1010
# Test database.
11-
TEST_DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/river_test?sslmode=disable
11+
TEST_DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/river_test?pool_max_conns=15&sslmode=disable
1212

1313
on:
1414
push:
@@ -42,6 +42,13 @@ jobs:
4242
postgres:
4343
image: postgres:${{ matrix.postgres-version }}
4444
env:
45+
# Left as a reminder that it might not be a bad idea to increase max
46+
# connections and then increase the maximum allowed in the databaes
47+
# pools for each package under tests. This config is only supported on
48+
# Postgres 16+ though, and changing Postgres configuration on any
49+
# version before that is absurdly difficult through Docker, so it
50+
# might be worth just waiting until <16 have rolled off.
51+
# POSTGRES_INITDB_ARGS: "-c max_connections=1500"
4552
POSTGRES_PASSWORD: postgres
4653
options: >-
4754
--health-cmd pg_isready
@@ -62,42 +69,11 @@ jobs:
6269
- name: Display Go version
6370
run: go version
6471

65-
- name: Set up test DBs
66-
run: go run ./internal/cmd/testdbman create
67-
env:
68-
PGHOST: 127.0.0.1
69-
PGPORT: 5432
70-
PGUSER: postgres
71-
PGPASSWORD: postgres
72-
PGSSLMODE: disable
72+
- name: Set up database
73+
run: psql -c "CREATE DATABASE river_test" $ADMIN_DATABASE_URL
7374

7475
- name: Test
75-
working-directory: .
76-
run: go test -p 1 -race ./... -timeout 2m
77-
78-
- name: Test cmd/river
79-
working-directory: ./cmd/river
80-
run: go test -race ./... -timeout 2m
81-
82-
- name: Test riverdriver
83-
working-directory: ./riverdriver
84-
run: go test -race ./... -timeout 2m
85-
86-
- name: Test riverdriver/riverdatabasesql
87-
working-directory: ./riverdriver/riverdatabasesql
88-
run: go test -race ./... -timeout 2m
89-
90-
- name: Test riverdriver/riverpgxv5
91-
working-directory: ./riverdriver/riverpgxv5
92-
run: go test -race ./... -timeout 2m
93-
94-
- name: Test rivershared
95-
working-directory: ./rivershared
96-
run: go test -race ./... -timeout 2m
97-
98-
- name: Test rivertype
99-
working-directory: ./rivertype
100-
run: go test -race ./... -timeout 2m
76+
run: make test/race
10177

10278
cli:
10379
strategy:
@@ -157,21 +133,16 @@ jobs:
157133

158134
- run: river migrate-get --all --exclude-version 1 --up
159135

160-
- name: river migrate-up
161-
run: river migrate-up --database-url $DATABASE_URL
162-
shell: bash
136+
- run: river migrate-up --database-url $DATABASE_URL
137+
shell: bash # needed for windows to interpret env var
163138

164-
- name: river migrate-list
165-
run: river migrate-list --database-url $DATABASE_URL
139+
- run: river migrate-list --database-url $DATABASE_URL
166140
shell: bash
167141

168-
- name: river validate
169-
run: river validate --database-url $DATABASE_URL
142+
- run: river validate --database-url $DATABASE_URL
170143
shell: bash
171144

172-
- name: river version
173-
run: river version
174-
shell: bash
145+
- run: river version
175146

176147
- name: river bench
177148
run: |
@@ -196,8 +167,7 @@ jobs:
196167
fi
197168
shell: bash
198169

199-
- name: river migrate-down
200-
run: river migrate-down --database-url $DATABASE_URL --max-steps 100
170+
- run: river migrate-down --database-url $DATABASE_URL --max-steps 100
201171
shell: bash
202172

203173
- name: river validate (expect failure)
@@ -214,6 +184,32 @@ jobs:
214184
fi
215185
shell: bash
216186

187+
#
188+
# Run the whole migration loop again, this time with a custom schema.
189+
#
190+
191+
- run: echo "CUSTOM_SCHEMA=custom_schema" >> $GITHUB_ENV
192+
shell: bash
193+
194+
- run: psql -c "CREATE SCHEMA $CUSTOM_SCHEMA" $DATABASE_URL
195+
shell: bash
196+
197+
- run: river migrate-up --database-url $DATABASE_URL --schema $CUSTOM_SCHEMA
198+
shell: bash
199+
200+
- run: river validate --database-url $DATABASE_URL --schema $CUSTOM_SCHEMA
201+
shell: bash
202+
203+
- run: river migrate-down --database-url $DATABASE_URL --max-steps 100 --schema $CUSTOM_SCHEMA
204+
shell: bash
205+
206+
- name: river validate (expect failure)
207+
run: |
208+
if river validate --database-url $DATABASE_URL --schema $CUSTOM_SCHEMA; then
209+
echo "expected non-zero exit code" && exit 1
210+
fi
211+
shell: bash
212+
217213
golangci:
218214
name: lint
219215
runs-on: ubuntu-latest

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ linters:
8888
- r
8989
- sb # common convention for string builder
9090
- t
91+
- tb
9192
- tt # common convention for table tests
9293
- tx
9394
- w

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
⚠️ Internal APIs used for communication between River and River Pro have changed. If using River Pro, make sure to update River and River Pro to latest at the same time to get compatible versions.
11+
1012
### Added
1113

1214
- Added `river/riverlog` containing middleware that injects a context logger to workers that collates log output and persists it with job metadata. This is paired with a River UI enhancement that shows logs in the UI. [PR #844](https://github.com/riverqueue/river/pull/844).
1315
- Added `JobInsertMiddlewareFunc` and `WorkerMiddlewareFunc` to easily implement middleware with a function instead of a struct. [PR #844](https://github.com/riverqueue/river/pull/844).
16+
- Added `Config.Schema` which lets a non-default schema be injected explicitly into a River client that'll be used for all database operations. This may be particularly useful for proxies like PgBouncer that may not respect a schema configured in `search_path`. [PR #848](https://github.com/riverqueue/river/pull/848).
1417

1518
### Changed
1619

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ $(foreach mod,$(submodules),$(eval $(call lint-target,$(mod))))
5353
.PHONY: test
5454
test:: ## Run test suite for all submodules
5555
define test-target
56-
test:: ; cd $1 && go test ./... -p 1
56+
test:: ; cd $1 && go test ./... -timeout 2m
5757
endef
5858
$(foreach mod,$(submodules),$(eval $(call test-target,$(mod))))
5959

6060
.PHONY: test/race
6161
test/race:: ## Run test suite for all submodules with race detector
6262
define test-race-target
63-
test/race:: ; cd $1 && go test ./... -p 1 -race
63+
test/race:: ; cd $1 && go test ./... -race -timeout 2m
6464
endef
6565
$(foreach mod,$(submodules),$(eval $(call test-race-target,$(mod))))
6666

0 commit comments

Comments
 (0)