laravel-transactional-events: Nested multiple connections problems.

Hi! I have many databases in one app. But there’s something wrong with laravel-transactional-events when nested multiple connections. I report these cases.

OK (Single connection)

\DB::connection('logdb')->transaction(function() {
    event(new \App\Events\EventA);
    event(new \App\Events\EventB);
});

Of course it is no problems. I got both events.

NG (Exception when nested multiple connections)

\DB::connection('logdb')->transaction(function() {
    \DB::transaction(function() {
        event(new \App\Events\EventA);
    });
    event(new \App\Events\EventB);
});

I got exception below.

ErrorException: count(): Parameter must be an array or an object that implements Countable in /vagrant/server/vendor/fntneves/laravel-transactional-events/src/Neves/Events/TransactionalDispatcher.php:152
Stack trace:
#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'count(): Parame...', '/vagrant/server...', 152, Array)
#1 /vagrant/server/vendor/fntneves/laravel-transactional-events/src/Neves/Events/TransactionalDispatcher.php(152): count(NULL)

NG (Inner event not fired when nested multiple connections)

\DB::connection('logdb')->transaction(function() {
    event(new \App\Events\EventB);
    \DB::transaction(function() {
        event(new \App\Events\EventA);
    });
});

I got EventB only. EventA is not fired.

My Environment

  • laravel 5.8.10
  • laravel-transactional-events 1.8.0
  • CentOS 7
  • MySQL 8.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 19 (8 by maintainers)

Commits related to this issue

Most upvoted comments

@fntneves just to let you know that I updated to Laravel 6 and it’s working great. Thanks for the package!

@azeos I will follow one of the proposed solutions from @ktanakaj. Events will be triggered only after the root transaction commits, which means that even when the inner transaction succeeds, if the root transaction fails, no event is fired at all. For such reason, I do not recommend to rely on events dispatched on nested transactions on distinct database services.

In detail, the concept of Transactional Event is not suitable for transactions of distinct connections, because, in the end, they are not effectively related. Considering the example given by @ktanakaj, if the transaction on logdb connection fails to commit, it will not rollback the inner transaction. At this point, should we fire the events fired by the inner transaction? If yes, application could end in an inconsistent state, because these events should not be dispatched before the previous ones (see example below, where LogConnectionEvent() is registered after CustomEvent()). Therefore, it makes no sense to handle transactions of distinct connections as if they were related, except if anything like distributed transactions (XA) is adopted.

DB::transaction(function(){
    event(new CustomEvent());

    DB::connection('log')->transaction(function() {
        event(new LogConnectionEvent());
    });

    event(new SecondCustomEvent());
})

I will update this package to fix the bug on using custom connections, but it will only dispatch events after the root transaction commits, even if the inner transaction commits.

@azeos Just to contextualise, the solution of this issue is more complex than it seems to be.

The thing is that dispatched events, in Laravel, are not related to connections. This package introduces that responsibility. Therefore, the logic to handle this has lots of edge cases in several scenarios that I must foresee.

Also, the fix must work for SAVEPOINTS scenarios, that is, nested transactions within the same connection and keep forcing that events are dispatched following the same order they were registered.

Give me some time to organise this, most of the code will be changed.

Thank you, of course, for your patience!

@azeos I am working on this issue. Hopefully, will push a new release during this weekend.