Startup timing issues w/ database initialization and evolutions

I’m running into what I think is a timing problem with database evolutions and startup of Play.

I have some actors bound using the AkkaGuiceSupport bindActor[]. These actors have some initialization code that accesses the database used by my application.

Under some cases it appears this initialization runs before the play evolution scripts run, so on a fresh install of the app I can get some table not found errors on first startup that don’t show up on subsequent start ups of the app.

I haven’t been able to find any information on stack overflow etc about dealing with start up time issues like this. Has anyone else run into a problem like this?

Thanks
-ryan

For anyone looking at this, I worked around the problem by injecting “ApplicationEvolutions” into objects requiring the database to be initialized on start up. Looks like the construction of ApplicationEvolutions doesn’t complete until all Evolutions have been applied.

I have the same problem. But for me injecting play.api.db.evolutions.ApplicationEvolutions into my Play controller did not change anything. The evolutions are still not completed when I try to access the database in the code.

I have this issue to track my investigation: https://github.com/playframework/play-slick/issues/504

This was likely due to a change in Play 2.7 to dev mode in how Play runs web commands (special Play commands like the button for running evolutions). Now dev mode evolutions are handled just before handling the request in the DefaultHttpRequestHandler.

I understand the motivation for this change, which was ultimately to give the user more control and simplify the logic in the Play dev server, but unfortunately it tends to break any apps that initialize database components eagerly on startup, or that have controllers that initialize components on initialization. This problem is made worse by the fact that the DefaultHttpRequestHandler depends on the application’s Router, which in turn depends on your controllers (unless you use @ to inject a provider), so the controllers and their dependencies need to be initialized first.

For what it’s worth, the change is mentioned in the migration guide, but it doesn’t mention that it could cause issues if you’re eagerly initializing database components.

In my opinion it should be possible to work around this by setting the usual play.evolutions.autoApply=true configuration. In that case Play doesn’t need to interact with the user at all, so it should be possible to run eagerly on startup when the ApplicationEvolutions component is initialized. The problem is that the logic in 2.7 has been changed to move even the auto-apply to the HttpRequestHandler, so this doesn’t work with default ApplicationEvolutions component.

Fortunately it’s possible to work around this. In my case I added an override in my application components:

  override lazy val applicationEvolutions: ApplicationEvolutions = new ApplicationEvolutions(
    evolutionsConfig,
    evolutionsReader,
    evolutionsApi,
    dynamicEvolutions,
    dbApi,
    environment.copy(mode = if (environment.mode == Mode.Dev) Mode.Prod else environment.mode),
    webCommands
  )
  applicationEvolutions

You could of course do the same using runtime DI by overriding the binding or replacing the EvolutionsModule. This basically tells ApplicationEvolutions to behave as if we’re in prod mode. In my particular use case this is fine, since I actually find it more convenient to have them auto-run in dev mode, and am comfortable using the configuration to manipulate the evolutions behavior.

I would argue there are two changes that could be made to improve the experience:

  1. Make play.evolutions.autoApply apply the evolutions eagerly on startup, without waiting for the application to load.
  2. Make the Router dependency of the HttpRequestHandler lazy, for example by injecting a => Router or Provider[Router]. This way it’s still possible to use the Play UI for running evolutions if your controllers require the database on initialization, as long as they’re initialized on demand.

I haven’t had as much time lately to contribute to Play, but I’ll start a discussion with the other maintainers to see if these changes make sense to implement.

1 Like