Skip to content

Commit 217b934

Browse files
Merge branch '8.x'
2 parents f6a4217 + 78e4538 commit 217b934

File tree

15 files changed

+1680
-24
lines changed

15 files changed

+1680
-24
lines changed

CHANGELOG-6.x.md

Lines changed: 1061 additions & 0 deletions
Large diffs are not rendered by default.

src/Illuminate/Database/Console/Seeds/SeedCommand.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Console\ConfirmableTrait;
77
use Illuminate\Database\ConnectionResolverInterface as Resolver;
88
use Illuminate\Database\Eloquent\Model;
9+
use Symfony\Component\Console\Input\InputArgument;
910
use Symfony\Component\Console\Input\InputOption;
1011

1112
class SeedCommand extends Command
@@ -90,7 +91,7 @@ public function handle()
9091
*/
9192
protected function getSeeder()
9293
{
93-
$class = $this->input->getOption('class');
94+
$class = $this->input->getArgument('class') ?? $this->input->getOption('class');
9495

9596
if (strpos($class, '\\') === false) {
9697
$class = 'Database\\Seeders\\'.$class;
@@ -118,6 +119,18 @@ protected function getDatabase()
118119
return $database ?: $this->laravel['config']['database.default'];
119120
}
120121

122+
/**
123+
* Get the console command arguments.
124+
*
125+
* @return array
126+
*/
127+
protected function getArguments()
128+
{
129+
return [
130+
['class', InputArgument::OPTIONAL, 'The class name of the root seeder', null],
131+
];
132+
}
133+
121134
/**
122135
* Get the console command options.
123136
*

src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function discoverEvents()
119119
->reduce(function ($discovered, $directory) {
120120
return array_merge_recursive(
121121
$discovered,
122-
DiscoverEvents::within($directory, base_path())
122+
DiscoverEvents::within($directory, $this->eventDiscoveryBasePath())
123123
);
124124
}, []);
125125
}
@@ -135,4 +135,14 @@ protected function discoverEventsWithin()
135135
$this->app->path('Listeners'),
136136
];
137137
}
138+
139+
/**
140+
* Get the base path to be used during event discovery.
141+
*
142+
* @return string
143+
*/
144+
protected function eventDiscoveryBasePath()
145+
{
146+
return base_path();
147+
}
138148
}

src/Illuminate/Queue/Console/WorkCommand.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class WorkCommand extends Command
3333
{--force : Force the worker to run even in maintenance mode}
3434
{--memory=128 : The memory limit in megabytes}
3535
{--sleep=3 : Number of seconds to sleep when no job is available}
36+
{--rest=0 : Number of seconds to rest between jobs}
3637
{--timeout=60 : The number of seconds a child process can run}
3738
{--tries=1 : Number of times to attempt a job before logging it failed}';
3839

@@ -143,7 +144,8 @@ protected function gatherWorkerOptions()
143144
$this->option('force'),
144145
$this->option('stop-when-empty'),
145146
$this->option('max-jobs'),
146-
$this->option('max-time')
147+
$this->option('max-time'),
148+
$this->option('rest')
147149
);
148150
}
149151

src/Illuminate/Queue/Middleware/ThrottlesExceptions.php

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@
88

99
class ThrottlesExceptions
1010
{
11+
/**
12+
* The developer specified key that the rate limiter should use.
13+
*
14+
* @var string
15+
*/
16+
protected $key;
17+
18+
/**
19+
* Indicates whether the throttle key should use the job's UUID.
20+
*
21+
* @var bool
22+
*/
23+
protected $byJob = false;
24+
1125
/**
1226
* The maximum number of attempts allowed before rate limiting applies.
1327
*
@@ -27,14 +41,7 @@ class ThrottlesExceptions
2741
*
2842
* @var int
2943
*/
30-
protected $retryAfterMinutes;
31-
32-
/**
33-
* The rate limiter key.
34-
*
35-
* @var string
36-
*/
37-
protected $key;
44+
protected $retryAfterMinutes = 0;
3845

3946
/**
4047
* The callback that determines if rate limiting should apply.
@@ -48,7 +55,7 @@ class ThrottlesExceptions
4855
*
4956
* @var string
5057
*/
51-
protected $prefix = 'circuit_breaker:';
58+
protected $prefix = 'laravel_throttles_exceptions:';
5259

5360
/**
5461
* The rate limiter instance.
@@ -62,15 +69,13 @@ class ThrottlesExceptions
6269
*
6370
* @param int $maxAttempts
6471
* @param int $decayMinutes
65-
* @param int $retryAfterMinutes
6672
* @param string $key
73+
* @return void
6774
*/
68-
public function __construct($maxAttempts = 10, $decayMinutes = 10, $retryAfterMinutes = 0, string $key = '')
75+
public function __construct($maxAttempts = 10, $decayMinutes = 10)
6976
{
7077
$this->maxAttempts = $maxAttempts;
7178
$this->decayMinutes = $decayMinutes;
72-
$this->retryAfterMinutes = $retryAfterMinutes;
73-
$this->key = $key;
7479
}
7580

7681
/**
@@ -129,6 +134,19 @@ public function withPrefix(string $prefix)
129134
return $this;
130135
}
131136

137+
/**
138+
* Specify the number of seconds a job should be delayed when it is released (before it has reached its max exceptions).
139+
*
140+
* @param int $backoff
141+
* @return $this
142+
*/
143+
public function backoff($backoff)
144+
{
145+
$this->retryAfterMinutes = $backoff;
146+
147+
return $this;
148+
}
149+
132150
/**
133151
* Get the cache key associated for the rate limiter.
134152
*
@@ -137,7 +155,38 @@ public function withPrefix(string $prefix)
137155
*/
138156
protected function getKey($job)
139157
{
140-
return $this->prefix.md5(empty($this->key) ? get_class($job) : $this->key);
158+
if ($this->key) {
159+
return $this->prefix.$this->key;
160+
} elseif ($this->byJob) {
161+
return $this->prefix.$job->job->uuid();
162+
}
163+
164+
return $this->prefix.md5(get_class($job));
165+
}
166+
167+
/**
168+
* Set the value that the rate limiter should be keyed by.
169+
*
170+
* @param string $key
171+
* @return $this
172+
*/
173+
public function by($key)
174+
{
175+
$this->key = $key;
176+
177+
return $this;
178+
}
179+
180+
/**
181+
* Indicate that the throttle key should use the job's UUID.
182+
*
183+
* @return $this
184+
*/
185+
public function byJob()
186+
{
187+
$this->byJob = true;
188+
189+
return $this;
141190
}
142191

143192
/**
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace Illuminate\Queue\Middleware;
4+
5+
use Illuminate\Container\Container;
6+
use Illuminate\Contracts\Redis\Factory as Redis;
7+
use Illuminate\Redis\Limiters\DurationLimiter;
8+
use Illuminate\Support\InteractsWithTime;
9+
use Throwable;
10+
11+
class ThrottlesExceptionsWithRedis extends ThrottlesExceptions
12+
{
13+
use InteractsWithTime;
14+
15+
/**
16+
* The Redis factory implementation.
17+
*
18+
* @var \Illuminate\Contracts\Redis\Factory
19+
*/
20+
protected $redis;
21+
22+
/**
23+
* The rate limiter instance.
24+
*
25+
* @var \Illuminate\Redis\Limiters\DurationLimiter
26+
*/
27+
protected $limiter;
28+
29+
/**
30+
* Process the job.
31+
*
32+
* @param mixed $job
33+
* @param callable $next
34+
* @return mixed
35+
*/
36+
public function handle($job, $next)
37+
{
38+
$this->redis = Container::getInstance()->make(Redis::class);
39+
40+
$this->limiter = new DurationLimiter(
41+
$this->redis, $this->getKey($job), $this->maxAttempts, $this->decayMinutes * 60
42+
);
43+
44+
if ($this->limiter->tooManyAttempts()) {
45+
return $job->release($this->limiter->decaysAt - $this->currentTime());
46+
}
47+
48+
try {
49+
$next($job);
50+
51+
$this->limiter->clear();
52+
} catch (Throwable $throwable) {
53+
if ($this->whenCallback && ! call_user_func($this->whenCallback, $throwable)) {
54+
throw $throwable;
55+
}
56+
57+
$this->limiter->acquire();
58+
59+
return $job->release($this->retryAfterMinutes * 60);
60+
}
61+
}
62+
}

src/Illuminate/Queue/Worker.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ public function daemon($connectionName, $queue, WorkerOptions $options)
156156
$jobsProcessed++;
157157

158158
$this->runJob($job, $connectionName, $options);
159+
160+
if ($options->rest > 0) {
161+
$this->sleep($options->rest);
162+
}
159163
} else {
160164
$this->sleep($options->sleep);
161165
}

src/Illuminate/Queue/WorkerOptions.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ class WorkerOptions
3939
*/
4040
public $sleep;
4141

42+
/**
43+
* The number of seconds to rest between jobs.
44+
*
45+
* @var int
46+
*/
47+
public $rest;
48+
4249
/**
4350
* The maximum amount of times a job may be attempted.
4451
*
@@ -87,14 +94,16 @@ class WorkerOptions
8794
* @param bool $stopWhenEmpty
8895
* @param int $maxJobs
8996
* @param int $maxTime
97+
* @param int $rest
9098
* @return void
9199
*/
92100
public function __construct($name = 'default', $backoff = 0, $memory = 128, $timeout = 60, $sleep = 3, $maxTries = 1,
93-
$force = false, $stopWhenEmpty = false, $maxJobs = 0, $maxTime = 0)
101+
$force = false, $stopWhenEmpty = false, $maxJobs = 0, $maxTime = 0, $rest = 0)
94102
{
95103
$this->name = $name;
96104
$this->backoff = $backoff;
97105
$this->sleep = $sleep;
106+
$this->rest = $rest;
98107
$this->force = $force;
99108
$this->memory = $memory;
100109
$this->timeout = $timeout;

src/Illuminate/Redis/Limiters/DurationLimiter.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,30 @@ public function acquire()
111111
return (bool) $results[0];
112112
}
113113

114+
/**
115+
* Determine if the key has been "accessed" too many times.
116+
*
117+
* @return bool
118+
*/
119+
public function tooManyAttempts()
120+
{
121+
[$this->decaysAt, $this->remaining] = $this->redis->eval(
122+
$this->tooManyAttemptsLuaScript(), 1, $this->name, microtime(true), time(), $this->decay, $this->maxLocks
123+
);
124+
125+
return $this->remaining <= 0;
126+
}
127+
128+
/**
129+
* Clear the limiter.
130+
*
131+
* @return void
132+
*/
133+
public function clear()
134+
{
135+
$this->redis->del($this->name);
136+
}
137+
114138
/**
115139
* Get the Lua script for acquiring a lock.
116140
*
@@ -143,6 +167,36 @@ protected function luaScript()
143167
end
144168
145169
return {reset(), ARGV[2] + ARGV[3], ARGV[4] - 1}
170+
LUA;
171+
}
172+
173+
/**
174+
* Get the Lua script to determine if the key has been "accessed" too many times.
175+
*
176+
* KEYS[1] - The limiter name
177+
* ARGV[1] - Current time in microseconds
178+
* ARGV[2] - Current time in seconds
179+
* ARGV[3] - Duration of the bucket
180+
* ARGV[4] - Allowed number of tasks
181+
*
182+
* @return string
183+
*/
184+
protected function tooManyAttemptsLuaScript()
185+
{
186+
return <<<'LUA'
187+
188+
if redis.call('EXISTS', KEYS[1]) == 0 then
189+
return {0, ARGV[2] + ARGV[3]}
190+
end
191+
192+
if ARGV[1] >= redis.call('HGET', KEYS[1], 'start') and ARGV[1] <= redis.call('HGET', KEYS[1], 'end') then
193+
return {
194+
redis.call('HGET', KEYS[1], 'end'),
195+
ARGV[4] - redis.call('HGET', KEYS[1], 'count')
196+
}
197+
end
198+
199+
return {0, ARGV[2] + ARGV[3]}
146200
LUA;
147201
}
148202
}

0 commit comments

Comments
 (0)