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

Exception thrown in DB::afterCommit cause "Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist" error #36886

Closed
midas104 opened this issue Apr 6, 2021 · 4 comments · Fixed by #36890
Assignees

Comments

@midas104
Copy link
Contributor

midas104 commented Apr 6, 2021

  • Laravel Version: 8.35.1
  • PHP Version: 8.0.3
  • Database Driver & Version: Percona 5.7

Description:

DB::afterCommit callbacks are executed before transaction level get decremented. So in case if callback do some actions inside it's own transaction and throws exception we will get Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist DB error (percona in my case)

Steps To Reproduce:

<?php

declare(strict_types=1);

use Illuminate\Contracts\Console\Kernel;

$app = require_once __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';

/** @var \App\Console\Kernel $kernel */
$kernel = $app->make(Kernel::class);
$kernel->bootstrap();

class SomeService
{
    public function mainBusinessLogic()
    {
        \DB::transaction(function () {
            // DO some business logic stuff here

            \DB::afterCommit(function () {
                $this->callSomeSecondaryBusinessLogic();
            });
        });
    }

    private function callSomeSecondaryBusinessLogic()
    {
        \DB::transaction(function () {
            // for instance this code should save some model
            // and dispatch job to db queue
            // so we need transaction here for sure
            throw  new \RuntimeException('tst');
        });
    }
}


(new SomeService())->mainBusinessLogic();
@midas104
Copy link
Contributor Author

midas104 commented Apr 6, 2021

Possible fix

            try {
                $this->transactions = max(0, $this->transactions - 1);

                if ($this->transactions == 0) {
                    $this->getPdo()->commit();

                    optional($this->transactionsManager)->commit($this->getName());
                }
            } catch (Throwable $e) {
                $this->handleCommitTransactionException(
                    $e, $currentAttempt, $attempts
                );

                continue;
            }

@taylorotwell
Copy link
Member

@themsaid any thoughts on this one?

@midas104
Copy link
Contributor Author

midas104 commented May 18, 2021

It seems we have one more issue around this

<?php

declare(strict_types=1);

use Illuminate\Contracts\Console\Kernel;

$app = require_once __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';

/** @var \App\Console\Kernel $kernel */
$kernel = $app->make(Kernel::class);
$kernel->bootstrap();


class SomeService
{
    public function mainBusinessLogic()
    {
        \DB::transaction(function () {
            // DO some business logic stuff here

            \DB::afterCommit(function () {
                echo 'after commit' . PHP_EOL;
                
                $this->callSomeSecondaryBusinessLogic();
            });
        });
    }

    private function callSomeSecondaryBusinessLogic()
    {
        \DB::transaction(function () {
            // for instance this code should save some model
            // and dispatch job to db queue
            // so we need transaction here for sure
        });
    }
}


(new SomeService())->mainBusinessLogic();

in success case we have infinity loop.

Laravel version: 8.40.0

@midas104
Copy link
Contributor Author

midas104 commented Jun 4, 2021

@themsaid any news on this ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants