Skip to content

EbeanPlugin: Add support for read-only data-sources #118

Closed

Description

@wsargent

Originally from playframework/playframework#3237

Currently (Play 2.2, Play 2.3) when using multiple data-sources then it is not possible to disable evolutions and DDL automatic generation. This is usually not a problem, but becomes a problem with at least one of the sources being read-only.

There is a PR for disabling evolutions for specific data-sources, but there is no way to disable DDL generation per data-source. Only way is to disable EbeanPlugin DDL generation globally like this:

evolutionplugin=disabled
This does not solve the problem, because we want evolutions for non-read-only datasources.

Currently, when evolutionplugin is enabled (the default behavior) then evolution scripts are generated automatically by using DDL extraction here. The scripts are created and created and since this is a read-only datasource then they cannot be applied.

There should be a data-source specific option to disable automatic DDL generation.

PS! The option name evolutionplugin is also misleading for this particular use-case.

Activity

  1. tamanugi commented on Jan 15, 2019

    @tamanugi

    Any progress on this issue?

    I got error

    Error trying to create the default EbeanServer
    java.lang.RuntimeException: DataSource user is null?
    	at org.avaje.datasource.pool.ConnectionPool.<init>(ConnectionPool.java:207)
    	at org.avaje.datasource.core.Factory.createPool(Factory.java:15)
    	at io.ebeaninternal.server.core.DefaultContainer.getDataSourceFromConfig(DefaultContainer.java:334)
    	at io.ebeaninternal.server.core.DefaultContainer.setDataSource(DefaultContainer.java:278)
    	at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:117)
    	at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:88)
    	at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:45)
    	at io.ebean.EbeanServerFactory.create(EbeanServerFactory.java:58)
    	at io.ebean.Ebean$ServerManager.getWithCreate(Ebean.java:202)
    	at io.ebean.Ebean$ServerManager.<init>(Ebean.java:160)
    	at io.ebean.Ebean$ServerManager.<init>(Ebean.java:133)
    	at io.ebean.Ebean.<clinit>(Ebean.java:127)
    
    package models;
    
    import io.ebean.config.ServerConfig;
    import io.ebean.event.ServerConfigStartup;
    import org.avaje.datasource.DataSourceConfig;
    
    public class MyServerConfigStartup implements ServerConfigStartup {
    
        @Override
        public void onStart(ServerConfig serverConfig) {
                serverConfig.setAutoReadOnlyDataSource(true);
            }
        }
    }

    Is there a workaround or correct setting?

    ref: ebean-orm/ebean#1232


    Java: 1.8.0_152
    Play: 2.6.13
    Play-Ebean: 4.1.3

  2. mkurz commented on Oct 8, 2026

    @mkurz
    Member

    This got closed by #901. Since this issue covers a few different scenarios, here's a summary of where things stand now:

    1. A read replica of the main database

    Since #892 you configure the replica as an additional Play database, and set it as the read-only data source of the Ebean server:

    db.default.url = "jdbc:postgresql://primary-host/app"
    db.replica.url = "jdbc:postgresql://replica-host/app"
    db.replica.hikaricp.readOnly = true
    
    ebean.default = ["models.*"]
    play.ebean.db.default.readOnlyDatasource = "replica"

    Ebean then runs queries outside of transactions, as well as explicitly read-only transactions, against the replica, using Play's connection pool for it. As the replica is not an Ebean server of its own, no evolutions script gets generated for it.

    This also replaces setAutoReadOnlyDataSource(true) from the comment above, which failed with DataSource user is null?: with that, Ebean creates a connection pool of its own for the read-only queries, and copies the user and password from its own main data source configuration. That one is empty though, because Play provides the main connection pool. (#566 ran into the same problem.)

    2. A separate read-only database, used as an Ebean server of its own (the original request)

    • To keep Play from applying evolutions to it, use Play's per-database setting play.evolutions.db.<name>.enabled = false (which did not exist yet when this issue was opened).
    • To keep Play Ebean from generating its evolutions script, use the new play.ebean.db.<name>.generateEvolutionsScripts = false (Allow turning evolutions script generation on or off per Ebean server #901).

    3. Other combinations

    play.ebean.db.<name>.generateEvolutionsScripts overrides the global play.ebean.generateEvolutionsScripts in both directions, and is independent of Play's evolutions settings on purpose, e.g. for:

    • a database whose evolutions come from the classpath (e.g. from a dependency): turn off the generation, because a generated conf/evolutions/<name>/1.sql takes precedence over those
    • a database to which Play shouldn't apply evolutions, but whose generated script is still wanted, e.g. to hand it to a DBA: keep the generation on, and turn off Play's evolutions for it
    • writing the evolutions yourself: remove the -- Created by Ebean DDL header from the generated script, after that Play Ebean doesn't touch it anymore

    The docs now describe all of this, see "Using a read replica" and "Generating evolution scripts".

    These changes are in main, 8.x and 7.x (#892/#893/#894 and #901/#902/#903), and will be part of the next releases. That's why I think this issue is resolved. If something is still missing for your use case, feel free to re-open it or open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions