-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add R2DBC support #2545
Merged
Merged
Add R2DBC support #2545
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
98515fc
Add R2DBC URL support
bsideup d3409df
fix a race
bsideup 3e2b34b
move javadoc to the top class
bsideup 84ce94d
use `auto-service`, add `testUrlSupport` test
bsideup 7b4a8c6
Add MSSQL R2DBC support
bsideup 8903ed8
Add MySQL
bsideup a3a5c5a
Add MariaDB
bsideup 886c9e7
Use `@AutoService` on `TestcontainersR2DBCConnectionFactoryProvider`
bsideup 184da88
Merge branch 'master' into r2dbc_support
bsideup 834dae8
add `AbstractR2DBCDatabaseContainerTest`
bsideup 937fe96
`TC_IMAGE` -> `TC_IMAGE_TAG`
bsideup 574df74
fix `reusesUntilConnectionFactoryIsClosed`
bsideup fa6a715
Merge branch 'master' into r2dbc_support
bsideup e145cc1
add docs
bsideup 830f483
Update docs/modules/databases/r2dbc.md
bsideup 3db13dc
Update docs/modules/databases/r2dbc.md
bsideup 38e81dc
Update docs/modules/databases/r2dbc.md
bsideup b28fb65
Merge branch 'master' into r2dbc_support
rnorth File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
...tgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.testcontainers.containers; | ||
|
||
import io.r2dbc.spi.ConnectionFactoryOptions; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.experimental.Delegate; | ||
import org.testcontainers.lifecycle.Startable; | ||
import org.testcontainers.r2dbc.R2DBCDatabaseContainer; | ||
|
||
@RequiredArgsConstructor | ||
public final class PostgreSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { | ||
|
||
@Delegate(types = Startable.class) | ||
private final PostgreSQLContainer<?> container; | ||
|
||
public static ConnectionFactoryOptions getOptions(PostgreSQLContainer<?> container) { | ||
ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() | ||
.option(ConnectionFactoryOptions.DRIVER, PostgreSQLR2DBCDatabaseContainerProvider.DRIVER) | ||
.build(); | ||
|
||
return new PostgreSQLR2DBCDatabaseContainer(container).configure(options); | ||
} | ||
|
||
@Override | ||
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { | ||
return options.mutate() | ||
.option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) | ||
.option(ConnectionFactoryOptions.PORT, container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)) | ||
.option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) | ||
.option(ConnectionFactoryOptions.USER, container.getUsername()) | ||
.option(ConnectionFactoryOptions.PASSWORD, container.getPassword()) | ||
.build(); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.testcontainers.containers; | ||
|
||
import io.r2dbc.spi.ConnectionFactoryOptions; | ||
import org.testcontainers.r2dbc.R2DBCDatabaseContainer; | ||
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; | ||
|
||
public final class PostgreSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { | ||
|
||
static final String DRIVER = "postgresql"; | ||
|
||
@Override | ||
public boolean supports(ConnectionFactoryOptions options) { | ||
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)); | ||
} | ||
|
||
@Override | ||
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { | ||
PostgreSQLContainer<?> container = new PostgreSQLContainer<>(options.getRequiredValue(IMAGE_OPTION)) | ||
.withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE)); | ||
|
||
if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) { | ||
container.withReuse(true); | ||
} | ||
return new PostgreSQLR2DBCDatabaseContainer(container); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
.../main/resources/META-INF/services/org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
org.testcontainers.containers.PostgreSQLR2DBCDatabaseContainerProvider |
42 changes: 42 additions & 0 deletions
42
...sql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.testcontainers.containers; | ||
|
||
import io.r2dbc.spi.Closeable; | ||
import io.r2dbc.spi.Connection; | ||
import io.r2dbc.spi.ConnectionFactories; | ||
import org.junit.Test; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
public class PostgreSQLR2DBCDatabaseContainerTest { | ||
|
||
@Test | ||
public void testGetOptions() { | ||
try (PostgreSQLContainer<?> container = new PostgreSQLContainer<>()) { | ||
container.start(); | ||
|
||
int result = Flux | ||
.usingWhen( | ||
Mono.just( | ||
ConnectionFactories.get( | ||
PostgreSQLR2DBCDatabaseContainer.getOptions(container) | ||
) | ||
), | ||
connectionFactory -> { | ||
return Flux | ||
.usingWhen( | ||
connectionFactory.create(), | ||
connection -> connection.createStatement("SELECT 42").execute(), | ||
Connection::close | ||
) | ||
.flatMap(it -> it.map((row, meta) -> (Integer) row.get(0))); | ||
}, | ||
it -> ((Closeable) it).close() | ||
) | ||
.blockFirst(); | ||
|
||
assertEquals(42, result); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="INFO"> | ||
<appender-ref ref="STDOUT"/> | ||
</root> | ||
|
||
<logger name="org.testcontainers" level="DEBUG"/> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
description = "Testcontainers :: R2DBC" | ||
|
||
dependencies { | ||
compile project(':testcontainers') | ||
compile 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' | ||
|
||
testCompile 'org.assertj:assertj-core:3.14.0' | ||
testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' | ||
testCompile project(':postgresql') | ||
} |
23 changes: 23 additions & 0 deletions
23
modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.testcontainers.r2dbc; | ||
|
||
import org.reactivestreams.Subscription; | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
class CancellableSubscription implements Subscription { | ||
|
||
private final AtomicBoolean cancelled = new AtomicBoolean(); | ||
|
||
@Override | ||
public void request(long n) { | ||
} | ||
|
||
@Override | ||
public void cancel() { | ||
cancelled.set(true); | ||
} | ||
|
||
public boolean isCancelled() { | ||
return cancelled.get(); | ||
} | ||
} |
161 changes: 161 additions & 0 deletions
161
modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package org.testcontainers.r2dbc; | ||
|
||
import io.r2dbc.spi.Connection; | ||
import io.r2dbc.spi.ConnectionFactory; | ||
import org.reactivestreams.Publisher; | ||
import org.reactivestreams.Subscriber; | ||
import org.reactivestreams.Subscription; | ||
|
||
import java.util.concurrent.CompletableFuture; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Design notes: | ||
* - ConnectionPublisher is Mono-like (0..1), the request amount is ignored | ||
* - given the testing nature, the performance requirements are less strict | ||
* - "synchronized" is used to avoid races | ||
* - Reactive Streams spec violations are not checked (e.g. non-positive request) | ||
*/ | ||
class ConnectionPublisher implements Publisher<Connection> { | ||
|
||
private final Supplier<CompletableFuture<ConnectionFactory>> futureSupplier; | ||
|
||
ConnectionPublisher(Supplier<CompletableFuture<ConnectionFactory>> futureSupplier) { | ||
this.futureSupplier = futureSupplier; | ||
} | ||
|
||
@Override | ||
public void subscribe(Subscriber<? super Connection> actual) { | ||
actual.onSubscribe(new StateMachineSubscription(actual)); | ||
} | ||
|
||
private class StateMachineSubscription implements Subscription { | ||
|
||
private final Subscriber<? super Connection> actual; | ||
|
||
Subscription subscriptionState; | ||
|
||
StateMachineSubscription(Subscriber<? super Connection> actual) { | ||
this.actual = actual; | ||
subscriptionState = new WaitRequestSubscriptionState(); | ||
} | ||
|
||
@Override | ||
public synchronized void request(long n) { | ||
subscriptionState.request(n); | ||
} | ||
|
||
@Override | ||
public synchronized void cancel() { | ||
subscriptionState.cancel(); | ||
} | ||
|
||
synchronized void transitionTo(SubscriptionState newState) { | ||
subscriptionState = newState; | ||
newState.enter(); | ||
} | ||
|
||
abstract class SubscriptionState implements Subscription { | ||
void enter() { | ||
} | ||
} | ||
|
||
class WaitRequestSubscriptionState extends SubscriptionState { | ||
|
||
@Override | ||
public void request(long n) { | ||
transitionTo(new WaitFutureCompletionSubscriptionState()); | ||
} | ||
|
||
@Override | ||
public void cancel() { | ||
} | ||
} | ||
|
||
class WaitFutureCompletionSubscriptionState extends SubscriptionState { | ||
|
||
private CompletableFuture<ConnectionFactory> future; | ||
|
||
@Override | ||
void enter() { | ||
this.future = futureSupplier.get(); | ||
|
||
future.whenComplete((connectionFactory, e) -> { | ||
if (e != null) { | ||
actual.onSubscribe(EmptySubscription.INSTANCE); | ||
actual.onError(e); | ||
return; | ||
} | ||
|
||
Publisher<? extends Connection> publisher = connectionFactory.create(); | ||
transitionTo(new ProxySubscriptionState(publisher)); | ||
}); | ||
} | ||
|
||
@Override | ||
public void request(long n) { | ||
} | ||
|
||
@Override | ||
public void cancel() { | ||
future.cancel(true); | ||
} | ||
} | ||
|
||
class ProxySubscriptionState extends SubscriptionState implements Subscriber<Connection> { | ||
|
||
private final Publisher<? extends Connection> publisher; | ||
|
||
private Subscription s; | ||
|
||
private boolean cancelled = false; | ||
|
||
ProxySubscriptionState(Publisher<? extends Connection> publisher) { | ||
this.publisher = publisher; | ||
} | ||
|
||
@Override | ||
void enter() { | ||
publisher.subscribe(this); | ||
} | ||
|
||
@Override | ||
public void request(long n) { | ||
// Ignore | ||
} | ||
|
||
@Override | ||
public synchronized void cancel() { | ||
cancelled = true; | ||
if (s != null) { | ||
s.cancel(); | ||
} | ||
} | ||
|
||
@Override | ||
public synchronized void onSubscribe(Subscription s) { | ||
this.s = s; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here we have to have a flag, e.g (canceled) and if so, immediately call s.cancel on the given one |
||
if (!cancelled) { | ||
s.request(1); | ||
} else { | ||
s.cancel(); | ||
} | ||
} | ||
|
||
@Override | ||
public void onNext(Connection connection) { | ||
actual.onNext(connection); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable t) { | ||
actual.onError(t); | ||
} | ||
|
||
@Override | ||
public void onComplete() { | ||
actual.onComplete(); | ||
} | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.testcontainers.r2dbc; | ||
|
||
import org.reactivestreams.Subscription; | ||
|
||
enum EmptySubscription implements Subscription { | ||
INSTANCE; | ||
|
||
@Override | ||
public void request(long n) { | ||
|
||
} | ||
|
||
@Override | ||
public void cancel() { | ||
|
||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that pattern was a bit harder for me to follow because of the habit of seeing
Subscription
as self-sufficient and thus aggressively guarded, but after review it looks correct. I feel it wouldn't be too hard to collapse the variousSubscriptionState
into theStateMachineSubscription
itself and deal with int/enum based states for transitions, but eh as long as it is correct...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking at it! FYI I just fixed a race reported by @OlegDokuka:
d3409df