Skip to content

Commit 99cf367

Browse files
Squirymp911de
authored andcommitted
pgbouncer integration tests
[#223][#225][#277]
1 parent 87462fc commit 99cf367

File tree

3 files changed

+140
-17
lines changed

3 files changed

+140
-17
lines changed

src/test/java/io/r2dbc/postgresql/client/ReactorNettyClientIntegrationTests.java

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import io.netty.util.ReferenceCountUtil;
2222
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
2323
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
24+
import io.r2dbc.postgresql.api.ErrorDetails;
2425
import io.r2dbc.postgresql.api.PostgresqlConnection;
26+
import io.r2dbc.postgresql.api.PostgresqlException;
2527
import io.r2dbc.postgresql.authentication.PasswordAuthenticationHandler;
2628
import io.r2dbc.postgresql.message.Format;
2729
import io.r2dbc.postgresql.message.backend.BackendMessage;
@@ -37,7 +39,9 @@
3739
import io.r2dbc.postgresql.message.frontend.FrontendMessage;
3840
import io.r2dbc.postgresql.message.frontend.Query;
3941
import io.r2dbc.postgresql.message.frontend.Sync;
42+
import io.r2dbc.postgresql.util.PgBouncer;
4043
import io.r2dbc.postgresql.util.PostgresqlServerExtension;
44+
import io.r2dbc.spi.R2dbcBadGrammarException;
4145
import io.r2dbc.spi.R2dbcNonTransientResourceException;
4246
import io.r2dbc.spi.R2dbcPermissionDeniedException;
4347
import org.junit.jupiter.api.AfterEach;
@@ -394,29 +398,77 @@ public boolean verify(String s, SSLSession sslSession) {
394398

395399
@Nested
396400
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
397-
final class StatementCacheSizeTests {
401+
final class PgBouncerTests {
398402

399403
@ParameterizedTest
400-
@ValueSource(ints = {0, 2, -1})
401-
void multiplePreparedStatementsTest(int statementCacheSize) {
402-
PostgresqlConnectionFactory connectionFactory = this.createConnectionFactory(statementCacheSize);
404+
@ValueSource(strings = {"transaction", "statement"})
405+
void disabledCacheWorksWithTransactionAndStatementModes(String poolMode) {
406+
try (PgBouncer pgBouncer = new PgBouncer(SERVER, poolMode)) {
407+
PostgresqlConnectionFactory connectionFactory = this.createConnectionFactory(pgBouncer, 0);
408+
409+
connectionFactory.create().flatMapMany(connection -> {
410+
Flux<Integer> q1 = connection.createStatement("SELECT 1 WHERE $1 = 1").bind(0, 1).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
411+
Flux<Integer> q2 = connection.createStatement("SELECT 2 WHERE $1 = 2").bind(0, 2).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
412+
Flux<Integer> q3 = connection.createStatement("SELECT 3 WHERE $1 = 3").bind(0, 3).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
413+
414+
return Flux.concat(q1, q1, q2, q2, q3, q3, connection.close());
415+
})
416+
.as(StepVerifier::create)
417+
.expectNext(1, 1, 2, 2, 3, 3)
418+
.verifyComplete();
419+
}
420+
}
403421

404-
connectionFactory.create().flatMapMany(connection -> {
405-
Flux<Integer> firstQuery = connection.createStatement("SELECT 1 WHERE $1 = 1").bind(0, 1).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
406-
Flux<Integer> secondQuery = connection.createStatement("SELECT 2 WHERE $1 = 2").bind(0, 2).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
407-
Flux<Integer> thirdQuery = connection.createStatement("SELECT 3 WHERE $1 = 3").bind(0, 3).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
422+
@ParameterizedTest
423+
@ValueSource(ints = {-1, 0, 2})
424+
void sessionModeWorksWithAllCaches(int statementCacheSize) {
425+
try (PgBouncer pgBouncer = new PgBouncer(SERVER, "session")) {
426+
PostgresqlConnectionFactory connectionFactory = this.createConnectionFactory(pgBouncer, statementCacheSize);
427+
428+
connectionFactory.create().flatMapMany(connection -> {
429+
Flux<Integer> q1 = connection.createStatement("SELECT 1 WHERE $1 = 1").bind(0, 1).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
430+
Flux<Integer> q2 = connection.createStatement("SELECT 2 WHERE $1 = 2").bind(0, 2).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
431+
Flux<Integer> q3 = connection.createStatement("SELECT 3 WHERE $1 = 3").bind(0, 3).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
432+
433+
return Flux.concat(q1, q1, q2, q2, q3, q3, connection.close());
434+
})
435+
.as(StepVerifier::create)
436+
.expectNext(1, 1, 2, 2, 3, 3)
437+
.verifyComplete();
438+
}
439+
}
408440

409-
return Flux.concat(firstQuery, secondQuery, thirdQuery, connection.close());
410-
})
411-
.as(StepVerifier::create)
412-
.expectNext(1, 2, 3)
413-
.verifyComplete();
441+
@ParameterizedTest
442+
@ValueSource(strings = {"transaction", "statement"})
443+
void statementCacheDoesntWorkWithTransactionAndStatementModes(String poolMode) {
444+
try (PgBouncer pgBouncer = new PgBouncer(SERVER, poolMode)) {
445+
PostgresqlConnectionFactory connectionFactory = this.createConnectionFactory(pgBouncer, -1);
446+
447+
connectionFactory.create().flatMapMany(connection -> {
448+
Flux<Integer> q1 = connection.createStatement("SELECT 1 WHERE $1 = 1").bind(0, 1).execute().flatMap(r -> r.map((row, rowMetadata) -> row.get(0, Integer.class)));
449+
450+
return Flux.concat(q1, q1, connection.close());
451+
})
452+
.as(StepVerifier::create)
453+
.expectNext(1)
454+
.verifyErrorMatches(e -> {
455+
if (!(e instanceof R2dbcBadGrammarException)) {
456+
return false;
457+
}
458+
if (!(e instanceof PostgresqlException)) {
459+
return false;
460+
}
461+
PostgresqlException pgException = (PostgresqlException) e;
462+
ErrorDetails errorDetails = pgException.getErrorDetails();
463+
return errorDetails.getCode().equals("26000") && errorDetails.getMessage().equals("prepared statement \"S_0\" does not exist");
464+
});
465+
}
414466
}
415467

416-
private PostgresqlConnectionFactory createConnectionFactory(int statementCacheSize) {
468+
private PostgresqlConnectionFactory createConnectionFactory(PgBouncer pgBouncer, int statementCacheSize) {
417469
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
418-
.host(SERVER.getHost())
419-
.port(SERVER.getPort())
470+
.host(pgBouncer.getHost())
471+
.port(pgBouncer.getPort())
420472
.username(SERVER.getUsername())
421473
.password(SERVER.getPassword())
422474
.database(SERVER.getDatabase())
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.r2dbc.postgresql.util;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.PostgreSQLContainer;
5+
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
6+
7+
public class PgBouncer implements AutoCloseable {
8+
9+
private final GenericContainer<?> container;
10+
11+
public PgBouncer(PostgresqlServerExtension server, String poolMode) {
12+
this.container = new GenericContainer<>("edoburu/pgbouncer")
13+
.withExposedPorts(PostgreSQLContainer.POSTGRESQL_PORT)
14+
.withEnv("POOL_MODE", poolMode)
15+
.withEnv("SERVER_RESET_QUERY_ALWAYS", "1")
16+
.withEnv("DB_USER", server.getUsername())
17+
.withEnv("DB_PASSWORD", server.getPassword())
18+
.withEnv("DB_HOST", server.getPostgres().getNetworkAlias())
19+
.withEnv("DB_PORT", String.valueOf(PostgreSQLContainer.POSTGRESQL_PORT))
20+
.withEnv("DB_NAME", server.getDatabase())
21+
.waitingFor(new HostPortWaitStrategy())
22+
.withNetwork(server.getPostgres().getNetwork());
23+
24+
this.container.start();
25+
}
26+
27+
@Override
28+
public void close() {
29+
this.container.stop();
30+
}
31+
32+
public String getHost() {
33+
return this.container.getContainerIpAddress();
34+
}
35+
36+
public int getPort() {
37+
return this.container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT);
38+
}
39+
}

src/test/java/io/r2dbc/postgresql/util/PostgresqlServerExtension.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public final class PostgresqlServerExtension implements BeforeAllCallback, After
5858
return PostgresqlServerExtension.containerInstance;
5959
}
6060

61+
PostgresqlServerExtension.containerNetwork = Network.newNetwork();
6162
return PostgresqlServerExtension.containerInstance = container();
6263
};
6364

@@ -186,6 +187,9 @@ public String getPassword() {
186187
return this.postgres.getPassword();
187188
}
188189

190+
public DatabaseContainer getPostgres() {
191+
return postgres;
192+
}
189193

190194
private <T extends PostgreSQLContainer<T>> T container() {
191195
T container = new PostgreSQLContainer<T>("postgres:latest")
@@ -196,7 +200,9 @@ private <T extends PostgreSQLContainer<T>> T container() {
196200
.withCopyFileToContainer(getHostPath("setup.sh", 0755), "/var/setup.sh")
197201
.withCopyFileToContainer(getHostPath("test-db-init-script.sql", 0755), "/docker-entrypoint-initdb.d/test-db-init-script.sql")
198202
.withReuse(true)
199-
.withCommand("/var/setup.sh");
203+
.withNetworkAliases("r2dbc-postgres")
204+
.withCommand("/var/setup.sh")
205+
.withNetwork(PostgresqlServerExtension.containerNetwork);
200206

201207
return container;
202208
}
@@ -226,13 +232,18 @@ interface DatabaseContainer {
226232

227233
String getHost();
228234

235+
@Nullable
236+
Network getNetwork();
237+
229238
int getPort();
230239

231240
String getDatabase();
232241

233242
String getUsername();
234243

235244
String getPassword();
245+
246+
String getNetworkAlias();
236247
}
237248

238249
/**
@@ -249,6 +260,12 @@ public String getHost() {
249260
return "localhost";
250261
}
251262

263+
@Override
264+
@Nullable
265+
public Network getNetwork() {
266+
return null;
267+
}
268+
252269
@Override
253270
public int getPort() {
254271
return 5432;
@@ -269,6 +286,11 @@ public String getPassword() {
269286
return "postgres";
270287
}
271288

289+
@Override
290+
public String getNetworkAlias() {
291+
return this.getHost();
292+
}
293+
272294
}
273295

274296
/**
@@ -289,6 +311,11 @@ public String getHost() {
289311
return this.container.getContainerIpAddress();
290312
}
291313

314+
@Override
315+
public Network getNetwork() {
316+
return this.container.getNetwork();
317+
}
318+
292319
@Override
293320
public int getPort() {
294321
return this.container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT);
@@ -308,5 +335,10 @@ public String getUsername() {
308335
public String getPassword() {
309336
return this.container.getPassword();
310337
}
338+
339+
@Override
340+
public String getNetworkAlias() {
341+
return "r2dbc-postgres";
342+
}
311343
}
312344
}

0 commit comments

Comments
 (0)