PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Gerencie o pool de conexões em um aplicativo web multilocatário com Spring, Hibernate e C3P0


Você pode escolher entre 3 estratégias diferentes que afetarão a pesquisa de conexão. Em qualquer caso, você deve fornecer uma implementação de MultiTenantConnectionProvider . A estratégia que você escolher afetará, obviamente, sua implementação.

Observação geral sobre MultiTenantConnectionProvider.getAnyConnection()

getAnyConnection() é exigido pelo hibernate para coletar metadados e configurar o SessionFactory. Normalmente, em uma arquitetura multilocatário, você tem um banco de dados especial/mestre (ou esquema) não usado por nenhum locatário. É uma espécie de banco de dados de modelo (ou esquema). Tudo bem se este método retornar uma conexão com este banco de dados (ou esquema).

Estratégia 1:cada locatário tem seu próprio banco de dados. (e, portanto, seu próprio pool de conexões)

Nesse caso, cada locatário tem seu próprio pool de conexões gerenciado pelo C3PO e você pode fornecer uma implementação de MultiTenantConnectionProvider com base em AbstractMultiTenantConnectionProvider

Cada locatário tem seu próprio C3P0ConnectionProvider , então tudo o que você precisa fazer em selectConnectionProvider(tenantIdentifier) é devolver o correto. Você pode manter um mapa para armazená-los em cache e inicializar com preguiça um C3POConnectionProvider com 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=...
    //     ...) 
}

Estratégia 2:cada locatário tem seu próprio esquema e seu próprio pool de conexões em um único banco de dados

Este caso é muito semelhante à primeira estratégia em relação ao ConnectionProvider implementação, pois você também pode usar AbstractMultiTenantConnectionProvider como classe base para implementar seu MultiTenantConnectionProvider

A implementação é muito semelhante à implementação sugerida para a Estratégia 1 exceto que você deve alterar o esquema em vez do banco de dados na configuração do c3po

Estratégia 3:cada locatário tem seu próprio esquema em um único banco de dados, mas usa um pool de conexões compartilhado

Esse caso é um pouco diferente, pois cada locatário usará o mesmo provedor de conexão (e, portanto, o pool de conexões será compartilhado). No caso :o provedor de conexão deve definir o esquema a ser usado antes de qualquer uso da conexão. ou seja, você deve implementar MultiTenantConnectionProvider.getConnection(String tenantIdentifier) (ou seja, a implementação padrão fornecida por AbstractMultiTenantConnectionProvider não funcionará).

Com postgresql você pode fazer isso com:
 SET search_path to <schema_name_for_tenant>;

ou usando o apelido
 SET schema <schema_name_for_tenant>;

Então aqui está o que seu getConnection(tenant_identifier); vai parecer:
@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;
}

Referência útil está aqui (documento oficial)

Outro link útil C3POConnectionProvider.java

Você pode combinar a estratégia 1 e a estratégia 2 em sua implementação. Você só precisa de uma maneira de encontrar as propriedades de conexão/url de conexão corretas para o locatário atual.

EDITAR

Acho que a escolha entre a estratégia 2 ou 3 depende do tráfego e do número de locatários em seu aplicativo. Com pools de conexão separados:a quantidade de conexões disponíveis para um locatário será muito menor e assim:se por algum motivo legítimo um locatário precisar de repente de muitas conexões, o desempenho visto por esse locatário específico diminuirá drasticamente (enquanto o outro locatário não será impactado).

Por outro lado, com a estratégia 3, se por algum motivo legítimo um locatário precisar repentinamente de muitas conexões:o desempenho visto por cada locatário diminuirá.

Em geral, acho que a estratégia 2 é mais flexível e segura:cada locatário não pode consumir mais do que uma determinada quantidade de conexão (e essa quantidade pode ser configurada por locatário se precisar)