Monday, April 1, 2013

Flyway and Cloud Foundry

Here is an interesting problem...

For work we're using flyway to migrate our databases at runtime. So far we're pretty happy about it, but we ran into a peculiar issue when attempting to use auto-reconfiguration features in Cloud Foundry.

For those who are unfamiliar, when you deploy a Spring webapp to Cloud Foundry, by default one bean of type javax.sql.DataSource is re-written at deployment to connect to the provisioned resource (in my case, a postgresql database). It's fairly nice in that I don't have to worry about what the username/password is for my JDBC connection, and when it works it allows me to focus on features instead of configuration

But if you have a flyway bean defined in your application context, most likely you have it as a dependency for your session factory or entity manager factory. Since flyway depends on your data source bean, it becomes ineligible for auto-reconfiguration by Cloud Foundry. You'll get something like the following error message:

Caused by: com.googlecode.flyway.core.api.FlywayException: Unable to obtain Jdbc connection from DataSource
        at com.googlecode.flyway.core.util.jdbc.JdbcUtils.openConnection(JdbcUtils.java:56)
        at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1232)
        at com.googlecode.flyway.core.Flyway.migrate(Flyway.java:820)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1581)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1522)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
        ... 38 more
Quite frustrating. So far I've only begun to understand the nature of this problem. The workaround would be to have your session factory or entity manager factory not depend on the flyway bean, but I'm not quite sure how we would go about ensuring the database is up-to-date.

*EDIT: it looks like this is the symptom, not the cause. Removing the flyway bean makes that error go away, but when I try to run a command that connects to the database I get the same issue. It's back to the drawing board for me.

*EDIT 2: So the short of the story is that if your dataSource bean is already a dependency of another bean that implements BeanFactoryPostProcessor, the dataSource bean is ineligible for the cloud foundry auto-reconfiguration support. That might not be worded 100% correctly, but that's the behavior I'm observing. Tomorrow I will attempt to explicitly configure my webapp for cloud foundry.

*EDIT 3: So this may have been an issue with the cloud foundry provider. I tried again the next day and everything worked as expected. So my theories above are incorrect, and dataSource should work just fine.