Skip to content

Commit 2f65ee1

Browse files
authored
Rate-Limit and Queue-Tweaks (#34)
* implement various tweaks to rate limits and queue * apply rate limit for all target ips
1 parent f446fdb commit 2f65ee1

File tree

7 files changed

+83
-57
lines changed

7 files changed

+83
-57
lines changed

app/Console/Commands/QueueUpdates.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function (Collection $chunk) use ($targetVersion) {
6363
$this->totalPushed += $chunk->count();
6464

6565
// Push each site check to queue
66-
$chunk->each(fn ($site) => UpdateSite::dispatch($site, $targetVersion));
66+
$chunk->each(fn ($site) => UpdateSite::dispatch($site, $targetVersion)->onQueue('updates'));
6767
}
6868
);
6969

app/Jobs/CheckSiteHealth.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,32 @@
88
use App\RemoteSite\Connection;
99
use App\TUF\TufFetcher;
1010
use Carbon\Carbon;
11+
use Illuminate\Contracts\Queue\ShouldBeUnique;
1112
use Illuminate\Contracts\Queue\ShouldQueue;
1213
use Illuminate\Foundation\Queue\Queueable;
1314
use Illuminate\Support\Facades\App;
1415

15-
class CheckSiteHealth implements ShouldQueue
16+
class CheckSiteHealth implements ShouldQueue, ShouldBeUnique
1617
{
1718
use Queueable;
1819

20+
public int $uniqueFor = 120;
21+
1922
/**
2023
* Create a new job instance.
2124
*/
2225
public function __construct(protected readonly Site $site)
2326
{
2427
}
2528

29+
/**
30+
* Get the unique ID for the job.
31+
*/
32+
public function uniqueId(): string
33+
{
34+
return (string) $this->site->id;
35+
}
36+
2637
/**
2738
* Execute the job.
2839
*/
@@ -66,6 +77,6 @@ public function handle(): void
6677
UpdateSite::dispatch(
6778
$this->site,
6879
$latestVersion
69-
);
80+
)->onQueue('updates');
7081
}
7182
}

app/Jobs/UpdateSite.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99
use App\RemoteSite\Connection;
1010
use App\RemoteSite\Responses\PrepareUpdate;
1111
use GuzzleHttp\Exception\RequestException;
12+
use Illuminate\Contracts\Queue\ShouldBeUnique;
1213
use Illuminate\Contracts\Queue\ShouldQueue;
1314
use Illuminate\Foundation\Queue\Queueable;
1415
use Illuminate\Support\Facades\App;
1516
use Illuminate\Support\Facades\Log;
1617

17-
class UpdateSite implements ShouldQueue
18+
class UpdateSite implements ShouldQueue, ShouldBeUnique
1819
{
1920
use Queueable;
2021
protected ?int $preUpdateCode = null;
22+
public int $uniqueFor = 3600;
2123

2224
/**
2325
* Create a new job instance.
@@ -26,6 +28,14 @@ public function __construct(protected readonly Site $site, protected string $tar
2628
{
2729
}
2830

31+
/**
32+
* Get the unique ID for the job.
33+
*/
34+
public function uniqueId(): string
35+
{
36+
return (string) $this->site->id;
37+
}
38+
2939
/**
3040
* Execute the job.
3141
*/

app/Providers/AppServiceProvider.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Providers;
44

5+
use App\Network\DNSLookup;
56
use Illuminate\Cache\RateLimiting\Limit;
67
use Illuminate\Http\Request;
78
use Illuminate\Support\Facades\RateLimiter;
@@ -23,10 +24,32 @@ public function register(): void
2324
public function boot(): void
2425
{
2526
RateLimiter::for('site', function (Request $request) {
26-
return Limit::perMinute(10)->by(
27-
// @phpstan-ignore-next-line
28-
parse_url((string) $request->input('url'), PHP_URL_HOST)
29-
);
27+
$siteHost = 'default';
28+
29+
if (is_string($request->input('url'))) {
30+
$siteHost = (string) parse_url($request->input('url'), PHP_URL_HOST);
31+
}
32+
33+
// Define a rate limit per target IP
34+
$siteIpLimits = [];
35+
36+
if ($siteHost !== 'default') {
37+
$siteIps = (new DNSLookup())->getIPs($siteHost);
38+
39+
foreach ($siteIps as $siteIp) {
40+
$siteIpLimits[] = Limit::perMinute(5)->by("siteip-" . $siteIp);
41+
}
42+
}
43+
44+
if (!count($siteIpLimits)) {
45+
$siteIpLimits = [Limit::perMinute(5)->by("siteip-default")];
46+
}
47+
48+
return [
49+
Limit::perMinute(5)->by("sitehost-" . $siteHost),
50+
Limit::perMinute(50)->by("requestip-" . $request->ip()),
51+
...$siteIpLimits
52+
];
3053
});
3154
}
3255
}

app/Providers/HttpclientServiceProvider.php

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33
namespace App\Providers;
44

55
use GuzzleHttp\Client;
6-
use GuzzleHttp\Exception\ConnectException;
7-
use GuzzleHttp\Handler\CurlHandler;
8-
use GuzzleHttp\HandlerStack;
9-
use GuzzleHttp\Middleware;
10-
use GuzzleHttp\Psr7\Request;
11-
use GuzzleHttp\Psr7\Response;
126
use Illuminate\Support\ServiceProvider;
137

148
class HttpclientServiceProvider extends ServiceProvider
@@ -21,42 +15,7 @@ class HttpclientServiceProvider extends ServiceProvider
2115
public function register()
2216
{
2317
$this->app->singleton(Client::class, function ($app) {
24-
$handlerStack = HandlerStack::create(new CurlHandler());
25-
$handlerStack->push(
26-
Middleware::retry(
27-
function (
28-
$retries,
29-
Request $request,
30-
Response $response = null,
31-
\Throwable $exception = null
32-
) {
33-
// Limit the number of retries to 3
34-
if ($retries >= 3) {
35-
return false;
36-
}
37-
38-
// Retry connection exceptions
39-
if ($exception instanceof ConnectException) {
40-
return true;
41-
}
42-
43-
if ($response) {
44-
// Retry on server errors
45-
if ($response->getStatusCode() >= 500) {
46-
return true;
47-
}
48-
}
49-
50-
return false;
51-
},
52-
function ($numberOfRetries) {
53-
return 1000 * $numberOfRetries;
54-
}
55-
)
56-
);
57-
5818
return new Client([
59-
'handler' => $handlerStack,
6019
'allow_redirects' => [
6120
'max' => 5,
6221
'strict' => true, // "strict" redirects - that's key as a redirected POST stays a POST

app/RemoteSite/Connection.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ public function performWebserviceRequest(
9494
"headers" => [
9595
"Content-Type" => "application/json",
9696
"Accept" => "application/vnd.api+json"
97-
]
97+
],
98+
'timeout' => 60.0,
99+
'connect_timeout' => 5.0,
98100
]
99101
);
100102

config/horizon.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@
180180
*/
181181

182182
'defaults' => [
183-
'supervisor-1' => [
183+
'supervisor-default' => [
184184
'connection' => 'redis',
185185
'queue' => ['default'],
186186
'balance' => 'auto',
@@ -190,22 +190,43 @@
190190
'maxJobs' => 0,
191191
'memory' => 128,
192192
'tries' => 1,
193-
'timeout' => 60,
193+
'timeout' => 15,
194+
'nice' => 0,
195+
],
196+
'supervisor-updates' => [
197+
'connection' => 'redis',
198+
'queue' => ['updates'],
199+
'balance' => 'auto',
200+
'autoScalingStrategy' => 'time',
201+
'maxProcesses' => 1,
202+
'maxTime' => 0,
203+
'maxJobs' => 0,
204+
'memory' => 128,
205+
'tries' => 1,
206+
'timeout' => 600,
194207
'nice' => 0,
195208
],
196209
],
197210

198211
'environments' => [
199212
'production' => [
200-
'supervisor-1' => [
201-
'maxProcesses' => 10,
202-
'balanceMaxShift' => 1,
203-
'balanceCooldown' => 3,
213+
'supervisor-default' => [
214+
'maxProcesses' => 250,
215+
'balanceMaxShift' => 10,
216+
'balanceCooldown' => 25,
217+
],
218+
'supervisor-updates' => [
219+
'maxProcesses' => 500,
220+
'balanceMaxShift' => 10,
221+
'balanceCooldown' => 25,
204222
],
205223
],
206224

207225
'local' => [
208-
'supervisor-1' => [
226+
'supervisor-default' => [
227+
'maxProcesses' => 3,
228+
],
229+
'supervisor-updates' => [
209230
'maxProcesses' => 3,
210231
],
211232
],

0 commit comments

Comments
 (0)