PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Gérer le regroupement de connexions dans une application Web multi-locataire avec Spring, Hibernate et C3P0

Vous pouvez choisir entre 3 stratégies différentes qui auront un impact sur l'interrogation de la connexion. Dans tous les cas, vous devez fournir une implémentation de MultiTenantConnectionProvider . La stratégie que vous choisissez aura bien sûr un impact sur votre mise en œuvre.

Remarque générale sur MultiTenantConnectionProvider.getAnyConnection()

getAnyConnection() est requis par hibernate pour collecter les métadonnées et configurer la SessionFactory. Généralement, dans une architecture multi-locataire, vous disposez d'une base de données (ou schéma) spéciale/maître qui n'est utilisée par aucun locataire. C'est une sorte de modèle de base de données (ou schéma). Ce n'est pas grave si cette méthode renvoie une connexion à cette base de données (ou schéma).

Stratégie 1 :chaque locataire a sa propre base de données. (et donc c'est son propre pool de connexion)

Dans ce cas, chaque locataire a son propre pool de connexions géré par C3PO et vous pouvez fournir une implémentation de MultiTenantConnectionProvider basé sur AbstractMultiTenantConnectionProvider

Chaque locataire a son propre C3P0ConnectionProvider , donc tout ce que vous avez à faire dans selectConnectionProvider(tenantIdentifier) est de retourner le bon. Vous pouvez conserver une Map pour les mettre en cache et vous pouvez initialiser paresseusement un C3POConnectionProvider avec quelque chose comme :

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=...
    //     ...) 
}

Stratégie 2 :chaque locataire a son propre schéma et son propre pool de connexion dans une seule base de données

Ce cas est très similaire à la première stratégie concernant ConnectionProvider mise en œuvre puisque vous pouvez également utiliser AbstractMultiTenantConnectionProvider comme classe de base pour implémenter votre MultiTenantConnectionProvider

L'implémentation est très similaire à l'implémentation suggérée pour la stratégie 1, sauf que vous devez modifier le schéma au lieu de la base de données dans la configuration c3po

Stratégie 3 :chaque locataire a son propre schéma dans une seule base de données mais utilise un pool de connexion partagé

Ce cas est légèrement différent puisque chaque locataire utilisera le même fournisseur de connexion (et donc le pool de connexion sera partagé). Dans le cas :le fournisseur de connexion doit définir le schéma à utiliser avant toute utilisation de la connexion. c'est-à-dire que vous devez implémenter MultiTenantConnectionProvider.getConnection(String tenantIdentifier) (c'est-à-dire l'implémentation par défaut fournie par AbstractMultiTenantConnectionProvider ne fonctionnera pas).

Avec postgresql vous pouvez le faire avec :

 SET search_path to <schema_name_for_tenant>;

ou en utilisant l'alias

 SET schema <schema_name_for_tenant>;

Voici donc ce que votre getConnection(tenant_identifier); ressemblera à :

@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 référence utile est ici (document officiel)

Autre lien utile C3POConnectionProvider.java

Vous pouvez combiner la stratégie 1 et la stratégie 2 dans votre implémentation. Vous avez juste besoin d'un moyen de trouver les propriétés de connexion/l'URL de connexion correctes pour le locataire actuel.

MODIFIER

Je pense que le choix entre la stratégie 2 ou 3 dépend du trafic et du nombre de tenants sur votre app. Avec des pools de connexions séparés :le nombre de connexions disponibles pour un locataire sera beaucoup plus faible et donc :si pour une raison légitime, un locataire a besoin soudainement de nombreuses connexions, les performances vues par ce locataire particulier diminueront considérablement (alors que l'autre locataire ne sera pas impacté).

En revanche, avec la stratégie 3, si pour une raison légitime un locataire a soudainement besoin de plusieurs connexions :les performances vues par chaque locataire diminueront.

En général, je pense que la stratégie 2 est plus flexible et plus sûre :chaque locataire ne peut pas consommer plus qu'une quantité donnée de connexion (et cette quantité peut être configurée par locataire si vous en avez besoin)