3 votes

Utilisation des répliques en lecture de Symfony 4, Doctrine et AWS RDS

Je suis sur le point de me lancer dans l'utilisation de RDS avec une configuration maître en lecture/écriture et esclave en lecture seule.

J'ai lu sur le type de connexion Doctrine MasterSlaveConnection.

Cependant, il y aura certains points d'extrémité que je créerai et pour lesquels je voudrais utiliser la réplique en lecture seule (la plupart de mes points d'extrémité GET) et d'autres qui devront écrire des données (points d'extrémité PUT, PATCH, DELETE).

J'utilise également la plate-forme API.

Quelle est la meilleure façon de réaliser ceci dans Symfony 4 et Doctrine 2 ?

1 votes

@qdequippe oui en termes de simple lecture de données (ce qui serait la plupart de mes requêtes GET REST API)

6voto

Chausser Points 2736

Ce que j'ai fait dans le passé, c'est d'utiliser des connexions différentes.

Quelque chose comme :

doctrine:
  dbal:
    default_connection: default
    connections:
      default:
        # This is your Master
        url: '%env(DATABASE_URL)%'
        driver: 'pdo_mysql'
        server_version: '5.7'
        charset: utf8mb4
      slave:
        # This would be the slave
        url: '%env(DATABASE_SLAVE_URL)%'
        driver: 'pdo_mysql'
        server_version: '5.7'
        charset: utf8mb4

  orm:
    default_entity_manager: default
    entity_managers:
      default:
        connection: default
        mappings:
          Main:
            is_bundle: false
            type: annotation
            dir: '%kernel.project_dir%/src/Entity'
            prefix: 'App\Entity'
            alias: Main
      slave:
        connection: slave
        mappings:
          Main:
            is_bundle: false
            type: annotation
            dir: '%kernel.project_dir%/src/Entity'
            prefix: 'App\Entity'
            alias: Main

https://symfony.com/doc/current/doctrine/multiple_entity_managers.html

Ensuite, dans vos contrôleurs ou votre logique métier, vous pouvez choisir d'utiliser le gestionnaire d'entités par défaut :

// Controller
$this->getDoctrine()->getEntityManager();

Ou vous pouvez obtenir la connexion d'esclave :

// Controller
$this->getDoctrine()->getEntityManager('slave');

Si vous avez besoin que cela fonctionne pour toutes les requêtes sans avoir à créer des actions spéciales pour chacune d'entre elles, votre meilleure option est de décorer les DataProviders Collection et Item pour la doctrine.

https://symfony.com/doc/current/service_container/service_decoration.html

https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/CollectionDataProvider.php

https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/ItemDataProvider.php

En fait, vous devez changer le gestionnaire qui est choisi en fonction de l'adresse de l'utilisateur. $opperationName quelque chose comme :

if($opperationName === 'GET'){
    $manager = $this->managerRegistry->getManager('slave');
} else {
    $manager = $this->managerRegistry->getManager();
}

0 votes

Merci, y a-t-il un moyen de lier cela à la plateforme API générée par CRUD ? Je comprends les bases de cette méthode et comment obtenir la connexion spécifique. Dans mes contrôleurs et actions écrits à la main, je sais comment choisir la bonne connexion. Mais j'utilise également API Platform pour certaines des ressources et j'aimerais pouvoir dicter quelles méthodes HTTP correspondent à quelle connexion. Comme je l'ai mentionné, PUT, PATCH, DELETE devraient utiliser le maître. GET n'aurait besoin que de l'esclave.

0 votes

@MattBoothDev J'ai mis à jour ma réponse avec une possibilité de faire cela plus globalement.

0 votes

Merci @Chase, j'essaierai demain.

5voto

zed Points 1807

Il n'est pas nécessaire de configurer plusieurs gestionnaires d'entités, et ce n'est pas non plus préférable, car il est difficile de gérer une entité avec plusieurs gestionnaires d'entités.

Avec Doctrine 2.2, vous pouvez installer des esclaves/répliques directement à partir de la configuration sans avoir besoin d'un gestionnaire d'entités supplémentaire :

Voir la référence de la configuration ici : https://www.doctrine-project.org/projects/doctrine-bundle/en/2.2/configuration.html#configuration-overview

Exemple :

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                dbname: '%env(DATABASE_DBNAME)%'
                user: '%env(DATABASE_USER)%'
                password: '%env(DATABASE_PASSWORD)%'
                host: '%env(DATABASE_HOST)%'
                driver: 'pdo_mysql'
                server_version: '5.7'
                charset: utf8mb4
                default_table_options:
                    charset: utf8mb4
                    collate: utf8mb4_unicode_ci
                slaves:
                    ro_replica:
                        dbname: '%env(REPLICA_DBNAME)%'
                        user: '%env(REPLICA_USER)%'
                        password: '%env(REPLICA_PASSWORD)%'
                        host: '%env(REPLICA_HOST)%'
                        charset: utf8mb4

1voto

Strabek Points 725

Merci @Chase pour la solution. Vous avez fait ma journée. Bien que cela fonctionne pour moi dans l'environnement 'dev', j'ai eu un problème lorsque je suis passé à 'prod'. Je recevais une erreur indiquant qu'une entité ne pouvait être trouvée. La solution vient de este post - merci @xabbuh. En gros, j'ai dû ajouter default_entity_manager: name_of_default_em à doctrine.yml. Voici la copie du code :

# config/packages/prod/doctrine.yaml
doctrine:
    orm:
        default_entity_manager: BOE <- add this line to let know prod about default em
        auto_generate_proxy_classes: false
        metadata_cache_driver:
            type: service
            id: doctrine.system_cache_provider
        query_cache_driver:
            type: service
            id: doctrine.system_cache_provider
        result_cache_driver:
            type: service
            id: doctrine.result_cache_provider

# ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X