Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when using ResetDatabase with DamaDoctrineTestBundle and Symfony Messenger with Doctrine #757

Closed
javiereguiluz opened this issue Dec 17, 2024 · 8 comments
Labels
bug Something isn't working

Comments

@javiereguiluz
Copy link
Contributor

javiereguiluz commented Dec 17, 2024

I'm facing the same issue that was reported in #658.

When using database reset, I see a lot of errors like this one:

PHP Fatal error:  Uncaught PDOException: SQLSTATE[HY000]: General error: 7 FATAL: 
terminating connection due to administrator command
server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request. in <my_project>/vendor/doctrine/dbal/src/Driver/PDO/Connection.php:33
Stack trace:
#0 <my_project>/vendor/doctrine/dbal/src/Driver/PDO/Connection.php(33): PDO->exec('UNLISTEN "messe...')
#1 <my_project>/vendor/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php(46): Doctrine\DBAL\Driver\PDO\Connection->exec('UNLISTEN "messe...')
#2 <my_project>/vendor/doctrine/dbal/src/Logging/Connection.php(50): Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware->exec('UNLISTEN "messe...')
#3 <my_project>/vendor/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php(46): Doctrine\DBAL\Logging\Connection->exec('UNLISTEN "messe...')
#4 <my_project>/vendor/symfony/doctrine-bridge/Middleware/Debug/DBAL3/Connection.php(73): Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware->exec('UNLISTEN "messe...')
#5 <my_project>/vendor/doctrine/dbal/src/Connection.php(1211): Symfony\Bridge\Doctrine\Middleware\Debug\DBAL3\Connection->exec('UNLISTEN "messe...')
#6 <my_project>/vendor/symfony/doctrine-messenger/Transport/Connection.php(425): Doctrine\DBAL\Connection->executeStatement('UNLISTEN "messe...', Array, Array)
#7 <my_project>/vendor/symfony/doctrine-messenger/Transport/PostgreSqlConnection.php(142): Symfony\Component\Messenger\Bridge\Doctrine\Transport\Connection->executeStatement('UNLISTEN "messe...')
#8 <my_project>/vendor/symfony/doctrine-messenger/Transport/PostgreSqlConnection.php(48): Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection->unlisten()
#9 [internal function]: Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection->__destruct()
#10 {main}

I'm using PostgreSQL for the database and these packages:

dama/doctrine-test-bundle            8.2.0
doctrine/dbal                        3.9.3
doctrine/doctrine-bundle             2.13.0
doctrine/orm                         2.20.0
symfony/messenger                    7.1.8
zenstruck/foundry                    2.3.1

The solution proposed in #658 is an extension mechanism that it's documented here: https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#extending-reset-mechanism

But, I don't know how to use that to solve the reported issue. Did anyone find any solution to this? Thanks!

@nikophil
Copy link
Member

nikophil commented Dec 17, 2024

Hi @javiereguiluz

few questions:

@javiereguiluz
Copy link
Contributor Author

javiereguiluz commented Dec 17, 2024

Yes, I'm using the dama/doctrine-test-bundle.

Commenting those lines solves the reported error ... but causes this other error that prevents running any tests:

RuntimeException: Error running "doctrine:database:drop --connection=default --force --if-exists": 
in <my_project>/vendor/zenstruck/foundry/src/symfony_console.php:27

Stack trace:
#0 <my_project>/vendor/zenstruck/foundry/src/ORM/ResetDatabase/BaseOrmResetter.php(78): Zenstruck\Foundry\runCommand(Object(Symfony\Bundle\FrameworkBundle\Console\Application), 'doctrine:databa...')
#1 <my_project>/vendor/zenstruck/foundry/src/ORM/ResetDatabase/SchemaDatabaseResetter.php(32): Zenstruck\Foundry\ORM\ResetDatabase\BaseOrmResetter->dropAndResetDatabase(Object(Symfony\Bundle\FrameworkBundle\Console\Application))
#2 <my_project>/vendor/zenstruck/foundry/src/ORM/ResetDatabase/DamaDatabaseResetter.php(46): Zenstruck\Foundry\ORM\ResetDatabase\SchemaDatabaseResetter->resetBeforeFirstTest(Object(App\Kernel))
#3 <my_project>/vendor/zenstruck/foundry/src/Persistence/ResetDatabase/ResetDatabaseManager.php(66): Zenstruck\Foundry\ORM\ResetDatabase\DamaDatabaseResetter->resetBeforeFirstTest(Object(App\Kernel))
#4 <my_project>/vendor/zenstruck/foundry/src/Test/ResetDatabase.php(35): Zenstruck\Foundry\Persistence\ResetDatabase\ResetDatabaseManager::resetBeforeFirstTest(Object(Closure), Object(Closure))
#5 <my_project>/vendor/bin/.phpunit/phpunit-9.6-0/src/Framework/TestSuite.php(629): App\Tests\Controller\ConferenceControllerTest::_resetDatabaseBeforeFirstTest()
#6 <my_project>/vendor/bin/.phpunit/phpunit-9.6-0/src/TextUI/TestRunner.php(651): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#7 <my_project>/vendor/bin/.phpunit/phpunit-9.6-0/src/TextUI/Command.php(146): PHPUnit\TextUI\TestRunner->run(Object(PHPUnit\Framework\TestSuite), Array, Array, true)
#8 <my_project>/vendor/bin/.phpunit/phpunit-9.6-0/src/TextUI/Command.php(99): PHPUnit\TextUI\Command->run(Array, true)
#9 <my_project>/vendor/bin/.phpunit/phpunit-9.6-0/phpunit(22): PHPUnit\TextUI\Command::main()
#10 <my_project>/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php(465): include('/Users/javier/w...')
#11 <my_project>/vendor/symfony/phpunit-bridge/bin/simple-phpunit(13): require('/Users/javier/w...')
#12 <my_project>/vendor/bin/simple-phpunit(119): include('/Users/javier/w...')
#13 {main}

@nikophil
Copy link
Member

nikophil commented Dec 17, 2024

I think I see where all of this is coming from: for PostgreSQL, we need to drop all other connections to the db (for example, any sql client which is connected to it) in order to be able to drop/recreate the connection. But if we do this, doctrine-messenger breaks when we call $kernel->shutdown(). This was basically your first problem.

On the other hand, if we don't drop all other connections, the connection from doctrine-messenger prevents to recreate the connection. This was your second problem, when you commented the code. And the real error is hidden by the message "Error running "doctrine:database:drop..."

I think we'll need to reproduce this in our CI, and see how everything can work together.

and, by the way:

The solution proposed in #658 is an extension mechanism

actually, in #658 the problem was between activating dama, and shutting down the kernel, and it was fixed inside #690 - the fix was not the extension mechanism, but a consequence of its implementation :)

@nikophil
Copy link
Member

nikophil commented Dec 17, 2024

@javiereguiluz I don't manage to reproduce your problem in our testsuite. In which context do you have your problem? do you have a messenger:consume command running?

I manage to dispatch a message with doctrine transport in a kernel test case, even when using database reset 🤔

@javiereguiluz
Copy link
Contributor Author

@nikophil sadly I won't have time to create a reproducer for this. but, I could fix it with the following change:

javiereguiluz@bda4e15

So, we're now using in the project my Foundry fork with that change. Do you know if there's some way to completely disable that DB reset logic without adding that return? Thanks!

@nikophil
Copy link
Member

sadly I won't have time to create a reproducer for this

ok, no problem. If at some point you have some time for this, I'd be willing to know what's going on and fix it in Foundry!

Do you know if there's some way to completely disable that DB reset logic without adding that return

yeah totally, you can decorate OrmResetter, and do something like this:

#[When('test')]
#[AsDecorator(OrmResetter::class)]
final readonly class DecorateDatabaseResetter implements OrmResetter
{
    public function __construct(
        private OrmResetter $decorated
    ) {}

    public function resetBeforeFirstTest(KernelInterface $kernel): void
    {
        // maybe do nothing here? or just call `doctrine:schema:update`?
    }

    public function resetBeforeEachTest(KernelInterface $kernel): void
    {
        $this->decorated->resetBeforeEachTest($kernel);
    }
}

@javiereguiluz
Copy link
Contributor Author

@nikophil your proposed solution worked beautifully, so we no longer need to use the fork. Thanks a lot.

Also, I started using this bundle just a few days ago. This error could totally be something related to bad configuration or usage on my side. So, let's close this issue for now as fixed. If other folks face the same issue, maybe they can add a comment here or create a new issue with a reproducer. Thanks!

@javiereguiluz javiereguiluz closed this as not planned Won't fix, can't repro, duplicate, stale Dec 18, 2024
@javiereguiluz
Copy link
Contributor Author

@nikophil a quick update about this.

I just finished updating all our existing tests to Foundry and the reported error is gone. We no longer need to decorate OrmResetter.

So, this "bug" is not really a bug and was caused for mixing Foundry and non-Foundry tests and for my lack of Foundry skills. Luckily, I'm improving at this 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

2 participants