Skip to content

Commit aca9367

Browse files
authored
Broken backward compatibility fix (#18)
1 parent 90e15dc commit aca9367

File tree

8 files changed

+270
-15
lines changed

8 files changed

+270
-15
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
vendor
2-
composer.lock
2+
composer.lock
3+
.phpunit.result.cache

README.md

+37
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,43 @@ worker runs it. Recurring jobs are specified much like other jobs:
166166

167167
```
168168

169+
### Sharding
170+
171+
If you compare Qless sharding with the DB sharding, then they have little in common.
172+
Qless gets random write-connection for all jobs.
173+
Data reading occurs in a circle from all connections.
174+
175+
Setup redis connections for sharding in `config/database.php`:
176+
```php
177+
return [
178+
'redis' => [
179+
// ...
180+
'qless' => [ /* ... */ ],
181+
'connection2' => [ /* ... */ ],
182+
'connection3' => [ /* ... */ ],
183+
// ...
184+
],
185+
];
186+
```
187+
188+
Then set the shards via `redis_connection` key in `config/queue.php` file:
189+
```php
190+
return [
191+
'connections' => [
192+
// ...
193+
'qless' => [
194+
// ...
195+
'redis_connection' => [
196+
'connection2',
197+
'connection3',
198+
],
199+
// ...
200+
],
201+
// ...
202+
],
203+
];
204+
```
205+
169206
## Testing
170207

171208
You can run the tests with:

config/queue.php

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
'driver' => 'qless',
99
'connection' => 'qless',
1010
'queue' => 'default',
11-
// 'redis_connection' => 'qless',
1211
'redis_connection' => ['qless1', 'qless2'],
1312
],
1413
],

src/Job/AbstractJob.php

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ protected function completeSync(): self
6161
return $this;
6262
}
6363

64+
/**
65+
* @throws \Exception
66+
*/
6467
private function completeImmediately(): void
6568
{
6669
$connector = new QlessConnector();
@@ -78,6 +81,9 @@ private function completeImmediately(): void
7881
$job->perform();
7982
}
8083

84+
/**
85+
* @throws \Exception
86+
*/
8187
public function __destruct()
8288
{
8389
if ($this->isSync) {

src/Queue/QlessConnectionHandler.php

+10-13
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,30 @@ class QlessConnectionHandler
1313
{
1414

1515
/** @var Client[] */
16-
private $clients;
16+
private $clients = [];
1717

1818
/** @var ArrayIterator */
1919
private $clientIterator;
2020

2121
/**
2222
* QlessConnectionHandler constructor.
2323
* @param Client[] $clients
24+
* @throws \Exception
2425
*/
2526
public function __construct(array $clients)
2627
{
2728
$this->init($clients);
2829
}
2930

31+
/**
32+
* @param array $clients
33+
* @throws \Exception
34+
*/
3035
private function init(array $clients): void
3136
{
32-
foreach ($clients as $client) {
33-
if (!$client instanceof Client) {
34-
continue;
35-
}
36-
$this->clients[] = $client;
37-
38-
}
37+
$this->clients = array_filter($clients, static function ($client) {
38+
return $client instanceof Client;
39+
});
3940
if (empty($this->clients)) {
4041
throw new \Exception("No configs found");
4142
}
@@ -55,11 +56,7 @@ public function getAllClients(): array
5556

5657
public function getCurrentClient(): Client
5758
{
58-
if ($this->clientIterator->current() === null) {
59-
return $this->getNextClient();
60-
}
61-
62-
return $this->clientIterator->current();
59+
return $this->clientIterator->current() ?? $this->getNextClient();
6360
}
6461

6562
public function getNextClient(): Client

src/Queue/QlessConnector.php

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class QlessConnector implements ConnectorInterface
2424
* @param array $config
2525
*
2626
* @return QlessQueue
27+
* @throws \Exception
2728
*/
2829
public function connect(array $config): QlessQueue
2930
{

src/Queue/QlessQueue.php

+9
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,13 @@ public function getCurrentConnection(): Client
310310
{
311311
return $this->clients->getCurrentClient();
312312
}
313+
314+
/**
315+
* @deprecated use \LaravelQless\Queue\QlessQueue::getCurrentConnection
316+
* @alias
317+
*/
318+
public function getConnection(): Client
319+
{
320+
return $this->getCurrentConnection();
321+
}
313322
}

tests/Job/QlessJobTest.php

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<?php
2+
3+
namespace LaravelQless\Tests\Job;
4+
5+
use Illuminate\Container\Container;
6+
use LaravelQless\Contracts\JobHandler;
7+
use LaravelQless\Queue\QlessQueue;
8+
use LaravelQless\Tests\Helpers\ModifierTrait;
9+
use Orchestra\Testbench\TestCase;
10+
use LaravelQless\Job\QlessJob;
11+
use Qless\Jobs\BaseJob;
12+
use Qless\Jobs\JobData;
13+
14+
class QlessJobTest extends TestCase
15+
{
16+
public function testGetJobId(): void
17+
{
18+
$jobMock = $this->getJob();
19+
$jobMock->method('getJid')
20+
->willReturn('my-test-jid');
21+
22+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $jobMock, ''));
23+
24+
self::assertEquals($job->getJobId(), 'my-test-jid');
25+
}
26+
27+
public function testGetData(): void
28+
{
29+
$jobMock = $this->getJob();
30+
$jobMock->method('getData')
31+
->willReturn(new JobData(['key' => 'value']));
32+
33+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $jobMock, ''));
34+
35+
self::assertEquals($job->getData(), ['key' => 'value']);
36+
}
37+
38+
public function testPayload(): void
39+
{
40+
$payload = '{"key": "value"}';
41+
42+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $this->getJob(), $payload));
43+
44+
self::assertEquals($job->payload(), ['key' => 'value']);
45+
}
46+
47+
public function testFireSuccess(): void
48+
{
49+
self::markTestSkipped('Refactor class to set `failed` property');
50+
}
51+
52+
public function testFireFailed(): void
53+
{
54+
self::markTestSkipped('Refactor class to set `failed` property');
55+
}
56+
57+
public function testRelease(): void
58+
{
59+
$queue = $this->getQueue();
60+
$queue->expects(self::once())
61+
->method('later')
62+
->willReturn('job-id');
63+
64+
$jobMock = $this->getJob();
65+
$jobMock->method('getData')
66+
->willReturn(new JobData(['key' => 'value']));
67+
68+
$job = (new QlessJob($this->getContainer(), $queue, $this->getJobHandler(), $jobMock, ''));
69+
70+
self::assertFalse($job->isReleased());
71+
72+
self::assertEquals($job->release(), 'job-id');
73+
74+
self::assertTrue($job->isReleased());
75+
}
76+
77+
public function testDelete(): void
78+
{
79+
$job = $this->getJob();
80+
$job->expects(self::once())
81+
->method('cancel');
82+
83+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
84+
85+
$job->delete();
86+
}
87+
88+
public function testAttempts(): void
89+
{
90+
$job = $this->getJob();
91+
$job->expects(self::once())
92+
->method('getRemaining')
93+
->willReturn(3);
94+
95+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
96+
97+
self::assertEquals($job->attempts(), 3);
98+
}
99+
100+
public function testMaxTries(): void
101+
{
102+
$job = $this->getJob();
103+
$job->expects(self::once())
104+
->method('getRetries')
105+
->willReturn(10);
106+
107+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
108+
109+
self::assertEquals($job->maxTries(), 10);
110+
}
111+
112+
public function testTimeout(): void
113+
{
114+
$job = $this->getJob();
115+
$job->expects(self::once())
116+
->method('ttl')
117+
->willReturn(123.0);
118+
119+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
120+
121+
self::assertEquals($job->timeout(), 123);
122+
}
123+
124+
/**
125+
* @depends testTimeout
126+
*/
127+
public function testTimeoutAt(): void
128+
{
129+
self::markTestSkipped('It is needed to refactor the class');
130+
}
131+
132+
public function testGetName(): void
133+
{
134+
$job = $this->getJob();
135+
$job->expects(self::once())
136+
->method('getKlass')
137+
->willReturn('jobClass');
138+
139+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
140+
141+
self::assertEquals($job->getName(), 'jobClass');
142+
}
143+
144+
public function testGetQueue(): void
145+
{
146+
$job = $this->getJob();
147+
$job->expects(self::once())
148+
->method('getQueue')
149+
->willReturn('jobName');
150+
151+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $job, ''));
152+
153+
self::assertEquals($job->getQueue(), 'jobName');
154+
}
155+
156+
public function testGetRawBody(): void
157+
{
158+
$payload = 'raw body';
159+
160+
$job = (new QlessJob($this->getContainer(), $this->getQueue(), $this->getJobHandler(), $this->getJob(), $payload));
161+
162+
self::assertEquals($job->getRawBody(), 'raw body');
163+
}
164+
165+
/**
166+
* @return Container|\PHPUnit\Framework\MockObject\MockObject
167+
*/
168+
private function getContainer()
169+
{
170+
return $this->getMockBuilder(Container::class)
171+
->disableOriginalConstructor()
172+
->getMock();
173+
}
174+
175+
/**
176+
* @return QlessQueue|\PHPUnit\Framework\MockObject\MockObject
177+
*/
178+
private function getQueue()
179+
{
180+
return $this->getMockBuilder(QlessQueue::class)
181+
->disableOriginalConstructor()
182+
->getMock();
183+
}
184+
185+
/**
186+
* @return JobHandler|\PHPUnit\Framework\MockObject\MockObject
187+
*/
188+
private function getJobHandler()
189+
{
190+
return $this->getMockBuilder(JobHandler::class)
191+
->disableOriginalConstructor()
192+
->getMock();
193+
}
194+
195+
/**
196+
* @return BaseJob|\PHPUnit\Framework\MockObject\MockObject
197+
*/
198+
private function getJob()
199+
{
200+
return $this->getMockBuilder(BaseJob::class)
201+
->disableOriginalConstructor()
202+
->getMock();
203+
}
204+
205+
}

0 commit comments

Comments
 (0)