1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! One PostgreSQL container per process, and a fresh database per test.
//!
//! # Why this exists
//!
//! §17 draws the line at storage fidelity: the properties worth testing here —
//! catalog compare-and-swap, advisory-lock scope, partition detach visibility,
//! `Decimal128` through Parquet — are exactly the ones a mock agrees with
//! whatever the code does. So the fixtures are real, and the cost is a
//! PostgreSQL.
//!
//! What is *not* required is a PostgreSQL **per test**. At a couple of hundred
//! integration tests that dominates the wall clock — every one paying a container
//! start, a health check and a connection handshake before it touches a line of
//! this crate — and it is flaky in a way that looks like a bug in the code under
//! test, since fifty containers racing for ports and memory produce connection
//! failures at whichever test happened to be starting.
//!
//! A container start is seconds; `CREATE DATABASE` is milliseconds, and gives
//! the **same** isolation. Every test still gets a database nothing else
//! touches, including its own Iceberg SQL catalog tables, so nothing about what
//! the suites prove changes.
//!
//! # The container is never dropped
//!
//! It lives in a process-wide `OnceCell` and outlives
//! every test, which is the point — a shared fixture that could be dropped by
//! whichever test finished first would be worse than no sharing at all.
//! `testcontainers` starts a reaper alongside it that removes the container when
//! the process exits, so nothing survives a run.
//!
//! # Opting out
//!
//! [`isolated_database`] starts a container of its own and hands back a guard.
//! Use it for a test that needs a server to itself — a restart, a configuration
//! change, a resource limit — where a shared instance would be a different
//! experiment.
use ;
use ;
use ImageExt;
use AsyncRunner;
use Postgres;
use OnceCell;
use crate;
/// The PostgreSQL image every suite runs against.
///
/// **Pinned, and not at the library default.** `testcontainers-modules` defaults
/// to `11-alpine`, one major below the floor this design requires (§3.1) — so
/// every suite would have been proving the store works on a version it does not
/// claim to support, and `ATTACH PARTITION` would still have taken
/// `ACCESS EXCLUSIVE` on the parent there, so the lock properties the tests
/// assert would have been asserted against the one server where they do not
/// hold. 16 matches the §18 reference deployment.
pub const IMAGE_TAG: &str = "16-alpine";
/// A running PostgreSQL, kept for the life of the process.
static SHARED: = const_new;
static NEXT_DATABASE: AtomicU64 = new;
/// Connections the shared server accepts.
///
/// PostgreSQL's default is 100, which was ample when every test had a container
/// to itself and is not when they share one: `cargo test` runs one thread per
/// core, each test opens one or more pools, and `sqlx` pools default to ten
/// connections each. Exhausting the limit does not produce a tidy "too many
/// clients" — the server drops the connection and `sqlx` reports
/// `expected to read 5 bytes, got 0 bytes at EOF`, which reads as a broken test.
///
/// Raising it is nearly free: an idle backend slot costs a few hundred bytes of
/// shared memory, and these are short-lived test connections.
const MAX_CONNECTIONS: &str = "500";
/// Start a PostgreSQL container and return its superuser URL.
async
/// Connect, retrying briefly while the server finishes coming up.
///
/// `testcontainers` waits for the readiness log line, which PostgreSQL emits
/// once during initdb *before* it restarts to accept real connections. The
/// window is short and the failure is a bare "connection refused" at whichever
/// test was unlucky — which reads as a defect in the code under test rather than
/// in the fixture, and is the more expensive kind of flake for exactly that
/// reason.
async
/// A connection URL for a database nothing else in this process uses.
///
/// The container is shared and started once; the database is created per call
/// and is as isolated as a separate server for everything these suites do — its
/// own tables, its own Iceberg SQL catalog, its own advisory-lock namespace is
/// the only thing it shares, and that is keyed by table name (§5.2).
pub async
/// A PostgreSQL nothing else shares, and the container keeping it alive.
///
/// Hold the guard for as long as the URL is in use; dropping it stops the
/// container. Prefer [`fresh_database`] unless the test genuinely needs a server
/// to itself.
/// Start a PostgreSQL container for one caller.
pub async