在Laravel中结合事务与队列

Is there a way to tell the Queue facade to not automatically persist what was pushed to the queue until I specifically tell it to?

Queue::noPersist()
Queue::push()
Queue::push()
Queue::persist()

See my problem is as follows, my code execution is wrapped in a transaction.

enter image description here

Inside this transaction is some eloquent records being added and right after also a job being pushed to the queue.

queue runs before transaction commits: The issue is that the queue sometimes starts running before my transaction finishes so it tries to reference a record that hasn't yet been "committed".

Provided my design is ok I'm thinking what I really need is for the queue to only submit after the transaction successfully commits: If the transaction fails then the job should also be rolled back so to speak. Or in other words the jobs shouldn't be pushed until after the transaction succeeds.

queue delay I saw laravel has a delay I can add to the queue but I didn't want to hack a random number in there as that's a bit fragile I really just want to only submit the queues if transaction succeeds.

Any guidance or built in features that could be useful to help me out here?

DB::transaction(function() {
    // add record A to DB
    // add JOB to QUEUE (this job starts firing before transaction commits and fails finding the record A)
    // more operations
});

Commit happens after your closure function, so if you keep using this structure - you won't be able to enqueue after commit.

However, Laravel got another way to do transactions (see 'Manually Using Transactions' in Laravel documentation). You could do something like:

$errors = false;

try {
  DB::beginTransaction();

  // All your operations here
} catch (Exception $e) {
  $errors = true;
  DB::rollBack();
}

if (!$errors) {
  DB::commit();

  // Add to queue
}

You can implement your own __wakeup method to handle the ModelNotFoundException raised by the SerializesModels trait, using something like this:

use Queueable, SerializesModels {
    __wakeup as wakeUp;
}

/**
 * Restore the model after serialization.
 *
 * @return void
 */
public function __wakeup()
{
    try
    {
        // Call SerializesModel __wakeup
        $this->wakeUp();
    }
    catch (Illuminate\Database\Eloquent\ModelNotFoundException $e)
    {
        // Delete job from the queue
        $this->delete();
    }
}

You can use my package: https://github.com/therezor/laravel-transactional-jobs

Simply add public $afterTransactions = true; to your job and it will be fired after transaction successfully committed.