0

I need to trigger a laravel job within the transaction.

Since the jobs are asynchronous, sometimes they complete before the transaction commits. In such situations, the job cannot get the relevant raw using the id. (Because the transaction is not yet committed and changes are not visible to the outside)

Please suggest a method other than putting this part outside of the transaction to solve this problem.

DB::beginTransaction()

...

$process = DB::table("trn_users")->insertGetId([
    "first_name" => $first_name,
    "last_name" => $last_name
]);

$job = (new SendEmailJob([
    'Table' => 'trn_users',
    'Id' => $process
]))->onQueue('email_send_job');
$this->dispatch($job);

...

DB:commit()
Toastrackenigma
  • 7,604
  • 4
  • 45
  • 55
  • 1
    Put the database update and the emailing in the queue and instead of queuing only the email send it synchronously (since the entire operation will be asynchronous now) – apokryfos Feb 18 '19 at 07:38
  • Do you want to rollback the insertion if email is not sent? Otherwise You can put dispatch email job after the commit, when the record is successfully inserted. – Mihir Bhende Feb 18 '19 at 07:39
  • Yes @MihirBhende I want to rollback the database change if anything goes wrong inside the job. – Prasanna Deshappriya Feb 18 '19 at 12:33
  • @apokryfos I face this issue in one of my latest project. I just simulate my issue by writing this small code. – Prasanna Deshappriya Feb 18 '19 at 12:37
  • 1
    I encountered a similar problem. A helpful package has been released here, that includes custom Bus\Dispatcher and Bus\QueueingDispatcher implementations that wait for transaction to commit: https://github.com/therezor/laravel-transactional-jobs – AlbinoDrought Sep 27 '19 at 20:26

2 Answers2

1

For this purpose I've published a package http://github.com/therezor/laravel-transactional-jobs

The other option is use events:

DB::beginTransaction()

...

$process = DB::table("trn_users")->insertGetId([
    "first_name" => $first_name,
    "last_name" => $last_name
]);

$job = (new SendEmailJob([
    'Table' => 'trn_users',
    'Id' => $process
]))->onQueue('email_send_job');

Event::listen(\Illuminate\Database\Events\TransactionCommitted::class, function () use ($job) {
      $this->dispatch($job);
});


...

DB:commit()
Roman REZOR
  • 91
  • 1
  • 4
0

I recently solved this problem in a project.

Simply defined a "buffer" facade singleton with a dispatch() method which instead of dispatching it right away, buffers jobs in memory until transaction commit.

When the 'buffer' class is constructed, it registers an event listener for commit and rollback events, and either dispatches or forgets buffered jobs depending on which event is fired.

It does some other clever stuff around the actual transaction level and working out whether it needs to buffer or dispatch immediately.

Hopefully you get the idea, but let me know if you want me to go into more detail.

Harry Lewis
  • 488
  • 5
  • 12