fix(server): avoid a burst of patch and Sentinel checks after the upgrade

The per-server schedules reused the cache keys of the old schedule, so
every server ran these checks at once on the first run after the
upgrade. New keys start fresh, so each server runs at its own slot.
This commit is contained in:
Andras Bacsai
2026-10-06 10:29:04 +02:00
parent c5bcb31f73
commit 460bb67d63
4 changed files with 20 additions and 7 deletions
@@ -211,8 +211,8 @@ class ScheduledJobDiagnostics extends Command
}
$dedupKeys = [
"server-patch-check:{$server->id}" => ServerManagerJob::patchCheckCron($server),
"sentinel-version-check:{$server->id}" => ServerManagerJob::sentinelVersionCheckCron($server),
"server-patch-check-v2:{$server->id}" => ServerManagerJob::patchCheckCron($server),
"sentinel-version-check-v2:{$server->id}" => ServerManagerJob::sentinelVersionCheckCron($server),
"server-check:{$server->id}" => isCloud() ? '*/5 * * * *' : '* * * * *',
"server-storage-check:{$server->id}" => data_get($server->settings, 'server_disk_usage_check_frequency', '0 23 * * *'),
];
+5 -3
View File
@@ -184,7 +184,7 @@ class ServerManagerJob implements ShouldBeEncrypted, ShouldQueue
// Unreachable servers are skipped: the SSH check would only fail. The connection check recovers them.
if ($server->isSentinelEnabled()
&& ! Cache::has(Server::sentinelReportedVersionCacheKey($server->id))
&& shouldRunCronNow(self::sentinelVersionCheckCron($server), $serverTimezone, "sentinel-version-check:{$server->id}", $this->executionTime)
&& shouldRunCronNow(self::sentinelVersionCheckCron($server), $serverTimezone, "sentinel-version-check-v2:{$server->id}", $this->executionTime)
&& $server->isFunctional()
) {
CheckAndStartSentinelJob::dispatch($server);
@@ -204,8 +204,10 @@ class ServerManagerJob implements ShouldBeEncrypted, ShouldQueue
}
}
// Dispatch ServerPatchCheckJob if due (weekly, staggered per server on Sunday)
$shouldRunPatchCheck = shouldRunCronNow(self::patchCheckCron($server), $serverTimezone, "server-patch-check:{$server->id}", $this->executionTime);
// Dispatch ServerPatchCheckJob if due (weekly, staggered per server on Sunday).
// The "-v2" keys start fresh: v4.3.23 stored its old run times under the unversioned keys, which
// would make every server run these checks at once on the first run after the upgrade.
$shouldRunPatchCheck = shouldRunCronNow(self::patchCheckCron($server), $serverTimezone, "server-patch-check-v2:{$server->id}", $this->executionTime);
if ($shouldRunPatchCheck) {
ServerPatchCheckJob::dispatch($server);
@@ -27,8 +27,8 @@ it('does not consume the dedup keys of the server manager checks', function () {
$this->artisan('scheduled:diagnostics', ['--type' => 'server-jobs'])->assertSuccessful();
expect(Cache::get("server-check:{$server->id}"))->toBeNull()
->and(Cache::get("server-patch-check:{$server->id}"))->toBeNull()
->and(shouldRunCronNow('0 0 * * 0', 'UTC', "server-patch-check:{$server->id}"))->toBeTrue();
->and(Cache::get("server-patch-check-v2:{$server->id}"))->toBeNull()
->and(shouldRunCronNow('0 0 * * 0', 'UTC', "server-patch-check-v2:{$server->id}"))->toBeTrue();
});
it('shows the occurrence state that the scheduled job manager records', function () {
@@ -9,6 +9,7 @@ use App\Models\Server;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Queue;
uses(RefreshDatabase::class);
@@ -113,3 +114,13 @@ it('dispatches the patch check once per week at the server slot on Sunday', func
runStaggerTestManagerAt($slot->copy()->addWeek());
Queue::assertPushed(ServerPatchCheckJob::class, 2);
});
it('does not run every patch check at once after an upgrade from the old weekly schedule', function () {
$server = createStaggerTestServer($this->team);
// v4.3.23 ran the patch check for all servers on Sunday at 00:00 under this key.
Cache::put("server-patch-check:{$server->id}", Carbon::parse('2025-01-12 00:00:00', 'UTC')->toIso8601String(), 2592000);
runStaggerTestManagerAt(Carbon::parse('2025-01-15 12:00:00', 'UTC'));
Queue::assertNotPushed(ServerPatchCheckJob::class);
});