How to use Master/Slave doctrine2 configurations from Symfony2 Console commands

10,891

Solution 1

You have to make sure your command doesn't call any action that lets doctrine choose the master connection.


Important for the understanding of the MasterSlaveConnection should be how and when it picks the slave or master.

  • picks Slave if Master was never picked before and ONLY if 'getWrappedConnection' or 'executeQuery' is used.
  • Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or 'prepare' is called.
  • If master was picked once during the lifetime of the connection it will always get picked afterwards.
  • One slave connection is randomly picked ONCE during a request.

(MasterSlaveConnection API)

Solution 2

I ended up on this question when I was looking for a way to query either my master for writting or my slaves for reading.

Here is the magic:

  • Write operations (use master): $em->getConnection()->executeUpdate(...);

  • Read operations (use slaves): $em->getConnection()->executeQuery(...);

Share:
10,891
timbostop
Author by

timbostop

Updated on June 13, 2022

Comments

  • timbostop
    timbostop almost 2 years

    I'm using the Doctrine2 master/slave configuration in Symfony2 to scale my application across a single master database and several read-only (replicated) slaves.

    This works perfectly from within my application and Doctrine2 sensibly uses the slave for read-only queries and the master for write operations.

    If in one of my controllers, I wrote:

    $em = $this->get('doctrine')->getManager();
    var_dump($em->getConnection()->isConnectedToMaster());
    

    isConnectedToMaster() returns false - which I would expect since the slave is connected to by default.

    This is also the case if I run this code inside a WebTestCase

    However if I wrote the exact same code inside a Symfony console command (ContainerAwareCommand):

    $em = $this->getContainer()->get('doctrine')->getManager();
    var_dump($em->getConnection()->isConnectedToMaster());
    

    isConnectedToMaster() returns true. Which means the master is selected as the default.

    I can't find out how to stop the default connection being master from console commands. Which means if I want to run some non-critical, heavy data processing tasks from the console - they all hit the master (bad) not one of the slaves (good).

    Anyone know how I can make Doctrine2 use the slave by default from the console? Or know why it always defaults to the master?

    ANSWERED: Thanks to nifr who set me on the right course - I found that my problem was because I was using JMS\JobQueueBundle (which replaces the Application used by app/console with an altered one which must connect to the MasterSlaveConnection in a way which forces the "master" to be chosen). When I commented out the

    use JMS\JobQueueBundle\Console\Application;
    

    in app/console, the slave was correctly selected in my console test.

    Thanks

  • timbostop
    timbostop about 10 years
    My point is that even within an "empty" command - ie a command that does nothing other than get the EntityManager and ask if it's connected to the master or slave - I'm finding that it's already connected to the master.
  • Nicolai Fröhlich
    Nicolai Fröhlich about 10 years
    You could debug wether one of the mentioned methods of MasterSlaveConnection is being called by putting breakpoints into each of them. Further you might want to put a breakpoint into the constructor to check wether the connection is actually being initialized. It could easily be that one of these methods is executed during app/console's bootstrapping. If the connection is actually constructed and none of the methods is being executed there might be a configuration problem.
  • timbostop
    timbostop about 10 years
    Do you have any tips for debugging in the app/console? I'm happy debugging (using PHPStorm) via the web interface but not in the console.
  • timbostop
    timbostop about 10 years
    I've edited my answer - it was my use of "JMS\JobQueueBundle\Console\Application;" in the app/console which was responsible for forcing the master. When I removed it, everything worked as expected. Thanks nifr
  • ZhukV
    ZhukV over 5 years
    How you control SELECT FOR UPDATE?
  • Alain Tiemblo
    Alain Tiemblo over 5 years
    you might be interested by this answer: stackoverflow.com/a/12995036/731138
  • Nico Haase
    Nico Haase over 2 years
    Please share more details. As far as I see, $em does not have any property called getConnection. Also, the documentation above the source code tells us that the master will always be used after it got used once, so read operations won't always use the slave