-
Notifications
You must be signed in to change notification settings - Fork 11.4k
[5.7] Transactional jobs #26515
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
[5.7] Transactional jobs #26515
Changes from all commits
5e0d634
fe03148
bf52c30
ada8ace
f8e7cd9
a0a9b32
af900a9
7849f25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
namespace Illuminate\Contracts\Queue; | ||
|
||
interface Transactional | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
namespace Illuminate\Queue\Jobs; | ||
|
||
use Illuminate\Support\InteractsWithTime; | ||
use Illuminate\Contracts\Queue\Transactional; | ||
|
||
abstract class Job | ||
{ | ||
|
@@ -80,7 +81,23 @@ public function fire() | |
|
||
[$class, $method] = JobName::parse($payload['job']); | ||
|
||
($this->instance = $this->resolve($class))->{$method}($this, $payload['data']); | ||
$this->instance = $this->resolve($class); | ||
|
||
$handler = function () use ($method, $payload) { | ||
$this->instance->{$method}($this, $payload['data']); | ||
}; | ||
|
||
if ($this->instance instanceof Transactional) { | ||
$handler = function () use ($handler) { | ||
$connection = method_exists($this->instance, 'dbConnection') && \is_callable([$this->instance, 'dbConnection']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How can we, except with documentation, better indicate to developers that this The interface doesn't indicate it. Also I'd like to see the naming a bit improved. E.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/laravel/framework/blob/5.7/src/Illuminate/Queue/Jobs/Job.php#L171 What about interface for this and another ones? |
||
? $this->instance->dbConnection() | ||
: null; | ||
|
||
$this->container->make('db')->getConnection($connection)->transaction($handler); | ||
}; | ||
} | ||
|
||
$handler(); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Queue; | ||
|
||
use Illuminate\Queue\SyncQueue; | ||
use PHPUnit\Framework\TestCase; | ||
use Illuminate\Queue\Jobs\SyncJob; | ||
use Illuminate\Container\Container; | ||
use Illuminate\Database\Connection; | ||
use Illuminate\Database\Capsule\Manager; | ||
use Illuminate\Contracts\Queue\Transactional; | ||
|
||
class QueueTransactionalJobTest extends TestCase | ||
{ | ||
/** | ||
* @param $jobClassname | ||
* @param $isTransactional | ||
* @throws \ReflectionException | ||
* | ||
* @dataProvider jobsDataProvider | ||
*/ | ||
public function testRunFireInDatabaseTransaction($jobClassname, $isTransactional, $connectionName) | ||
{ | ||
$connection = $this->createMock(Connection::class); | ||
$connection->expects($isTransactional ? $this->once() : $this->never()) | ||
->method('transaction') | ||
->willReturnCallback(function ($handler) { | ||
$handler(); | ||
}); | ||
|
||
$db = $this->createMock(Manager::class); | ||
$db->expects($isTransactional ? $this->once() : $this->never()) | ||
->method('getConnection') | ||
->with($connectionName) | ||
->willReturn($connection); | ||
|
||
/** @var Container|\PHPUnit_Framework_MockObject_MockObject $container */ | ||
$container = $this->createMock(Container::class); | ||
$container->expects($this->exactly($isTransactional ? 2 : 1)) | ||
->method('make') | ||
->willReturnCallback(function ($abstract) use ($db) { | ||
switch ($abstract) { | ||
case 'db': | ||
return $db; | ||
default: | ||
return new $abstract; | ||
} | ||
}); | ||
|
||
$payloadFactory = new \ReflectionMethod(SyncQueue::class, 'createPayload'); | ||
$payloadFactory->setAccessible(true); | ||
|
||
$payload = $payloadFactory->invoke(new SyncQueue(), $jobClassname, null, ''); | ||
|
||
unset($_SERVER['0(*_*)0']); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we do better then working with a global variable here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, job can use any payload, given from container mock to detect works. It also can use real DB connection, real transactions. It's only base ones. |
||
$job = new SyncJob($container, $payload, '', ''); | ||
$job->fire(); | ||
$this->assertEquals('0(*_*)0', $_SERVER['0(*_*)0']); | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function jobsDataProvider(): array | ||
{ | ||
return [ | ||
[TransactionalWithCustomConnection::class, true, 'custom'], | ||
[TransactionalJob::class, true, null], | ||
[SimpleJob::class, false, null], | ||
]; | ||
} | ||
} | ||
|
||
class SimpleJob | ||
{ | ||
public function fire() | ||
{ | ||
$_SERVER['0(*_*)0'] = '0(*_*)0'; | ||
} | ||
} | ||
|
||
class TransactionalJob extends SimpleJob implements Transactional | ||
{ | ||
} | ||
|
||
class TransactionalWithCustomConnection extends TransactionalJob | ||
{ | ||
public function dbConnection(): string | ||
{ | ||
return 'custom'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH this style is freaking me out: "use"ing a variable and overwriting it in the same scope so the final call to it is the same? => That's a bit too much magic for my taste