sql >> Base de Datos >  >> RDS >> PostgreSQL

Administre la agrupación de conexiones en una aplicación web multiusuario con Spring, Hibernate y C3P0

Puede elegir entre 3 estrategias diferentes que afectarán el sondeo de conexión. En cualquier caso, debe proporcionar una implementación de MultiTenantConnectionProvider . La estrategia que elija, por supuesto, afectará su implementación.

Comentario general sobre MultiTenantConnectionProvider.getAnyConnection()

getAnyConnection() Hibernate lo requiere para recopilar metadatos y configurar SessionFactory. Por lo general, en una arquitectura de múltiples inquilinos, tiene una base de datos (o esquema) especial/maestra que no utiliza ningún inquilino. Es una especie de plantilla de base de datos (o esquema). Está bien si este método devuelve una conexión a esta base de datos (o esquema).

Estrategia 1:cada inquilino tiene su propia base de datos. (y por lo tanto es su propio grupo de conexiones)

En este caso, cada inquilino tiene su propio conjunto de conexiones administrado por C3PO y puede proporcionar una implementación de MultiTenantConnectionProvider basado en AbstractMultiTenantConnectionProvider

Cada inquilino tiene su propio C3P0ConnectionProvider , por lo que todo lo que tiene que hacer en selectConnectionProvider(tenantIdentifier) es devolver el correcto. Puede mantener un Mapa para almacenarlos en caché y puede iniciar de forma diferida un C3POConnectionProvider con algo como:

private ConnectionProvider lazyInit(String tenantIdentifier){
    C3P0ConnectionProvider connectionProvider = new C3P0ConnectionProvider();
    connectionProvider.configure(getC3POProperties(tenantIdentifier));
    return connectionProvider;
}

private Map getC3POProperties(String tenantIdentifier){
    // here you have to get the default hibernate and c3po config properties 
    // from a file or from Spring application context (there are good chances
    // that those default  properties point to the special/master database) 
    // and alter them so that the datasource point to the tenant database
    // i.e. : change the property hibernate.connection.url 
    // (and any other tenant specific property in your architecture like :
    //     hibernate.connection.username=tenantIdentifier
    //     hibernate.connection.password=...
    //     ...) 
}

Estrategia 2:cada inquilino tiene su propio esquema y su propio grupo de conexiones en una sola base de datos

Este caso es muy similar a la primera estrategia con respecto a ConnectionProvider implementación ya que también puede usar AbstractMultiTenantConnectionProvider como clase base para implementar su MultiTenantConnectionProvider

La implementación es muy similar a la implementación sugerida para la Estrategia 1, excepto que debe modificar el esquema en lugar de la base de datos en la configuración de c3po

Estrategia 3:cada inquilino tiene su propio esquema en una sola base de datos pero usa un grupo de conexiones compartidas

Este caso es ligeramente diferente, ya que cada arrendatario utilizará el mismo proveedor de conexión (y, por lo tanto, se compartirá el conjunto de conexiones). En el caso:el proveedor de conexión debe configurar el esquema para usar antes de cualquier uso de la conexión. es decir, debe implementar MultiTenantConnectionProvider.getConnection(String tenantIdentifier) (es decir, la implementación predeterminada proporcionada por AbstractMultiTenantConnectionProvider no funcionará).

Con postgresql puedes hacerlo con:

 SET search_path to <schema_name_for_tenant>;

o usando el alias

 SET schema <schema_name_for_tenant>;

Así que esto es lo que su getConnection(tenant_identifier); se verá como:

@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
    final Connection connection = getAnyConnection();
    try {
        connection.createStatement().execute( "SET search_path TO " + tenanantIdentifier );
    }
    catch ( SQLException e ) {
        throw new HibernateException(
                "Could not alter JDBC connection to specified schema [" +
                        tenantIdentifier + "]",
                e
        );
    }
    return connection;
}

La referencia útil está aquí (documento oficial)

Otro enlace útil C3POConnectionProvider.java

Puede combinar la estrategia 1 y la estrategia 2 en su implementación. Solo necesita una forma de encontrar las propiedades de conexión/URL de conexión correctas para el inquilino actual.

EDITAR

Creo que la elección entre la estrategia 2 o 3 depende del tráfico y la cantidad de inquilinos en su aplicación. Con grupos de conexiones separados:la cantidad de conexiones disponibles para un arrendatario será mucho menor y, por lo tanto:si por alguna razón legítima un arrendatario necesita repentinamente muchas conexiones, el rendimiento observado por este arrendatario en particular disminuirá drásticamente (mientras que el otro arrendatario no será impactado).

Por otro lado, con la estrategia 3, si por alguna razón legítima un arrendatario necesita repentinamente muchas conexiones:el rendimiento visto por cada arrendatario disminuirá.

En general, creo que la estrategia 2 es más flexible y segura:cada arrendatario no puede consumir más de una determinada cantidad de conexión (y esta cantidad se puede configurar por arrendatario si lo necesita)