I was testing out R2DBC inside spring boot with kotlin's coroutines - and it was working fine while I was developing it.
The develop setup was simple - a docker container for the actual postgres database - exposing the standard port 5432 on localhost - and then just run the kotlin boot app.
However - once I wanted to add this to staging - the setup changes - the following were set up in docker:
And suddenly - although flyway could talk to the database via jdbc - the r2dbc connection was failing.
The connection string looked like this:
r2dbc:pool:postgresql://${DB_HOST}:${DB_PORT}/db_name
So - I had to create a test harness to see what was going on.
First up - you need to build the application:
mvn clean package
Development
Now - let's try running it as if we were in development:
We'll start up a local db container for testing (with some options to make sure it's a clean instance)
docker-compose -f docker-compose-test-1.yml up --force-recreate -V
And then run the appliation
DB_HOST=localhost DB_PORT=5432 DB_USER=test DB_PASSWORD=test java -jar target/test-pg-r2dbc.jar
The application runs and you should get something like this at the end of the logs:
YYYY-MM-DD HH:MM:SS.SSS INFO THREADID --- [ main] n.c.testpgr2dbc.TestPgR2dbcApplication : Sample(id=1, name=Name)
YYYY-MM-DD HH:MM:SS.SSS INFO THREADID --- [tor-tcp-epoll-1] n.c.testpgr2dbc.TestPgR2dbcApplication : Sample(id=1, name=Name)
Staging/Production
So let's mimic a staging setup.
First - we need to package the app into docker:
docker build -t test-pg-r2dbc:latest .
Now we can run up the more complex docker setup:
docker-compose -f docker-compose-test-2.yml up --force-recreate -V
This time the logs show postgresql starting then the app - which then errors:
Cannot connect to app_db:5432/<unresolved>:5432
and deeper in the stacktrace:
Caused by: java.net.UnknownHostException: app_db:5432: invalid IPv6 address
Very strange.
Prefer IPv4?
Some googling suggested that they have had issues with IPv6 in r2dbc-postgresql before - and they suggest setting the following JVM param:
-Djava.net.preferIPv4Stack=true
However - the results were exactly the same.
Workaround
What does seem to work however - is removing the port from the r2dbc URL.
In application.yml - change:
spring:
r2dbc:
url: r2dbc:pool:postgresql://${DB_HOST}:${DB_PORT}/test_db
to
spring:
r2dbc:
url: r2dbc:pool:postgresql://${DB_HOST}/test_db
Now - we just need to rebuild:
mvn package
docker build -t test-pg-r2dbc:latest .
And run again:
docker-compose -f docker-compose-test-2.yml up --force-recreate -V
And this time - it works:
app | YYYY-MM-DD HH:MM:SS.SSS INFO 1 --- [ main] n.c.testpgr2dbc.TestPgR2dbcApplication : Sample(id=1, name=Name)
app | YYYY-MM-DD HH:MM:SS.SSS INFO 1 --- [tor-tcp-epoll-1] n.c.testpgr2dbc.TestPgR2dbcApplication : Sample(id=1, name=Name)
This does feel like a bug in r2dbc-postgresql - but I'm not entirely sure - this was written trying to work out how r2dbc works - so I am not sure if it is completely correct - but - at least it now works.
Is it an issue having to use the default port?
Not really - since I run each application under docker on its own docker network and with a container for each postgres DB (or other DB) so there should be no conflicts.