From 14d40c73a2204558ab1d6e3ed2d7897e2dc5e15a Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:47:19 +0200 Subject: [PATCH] fix(docker-cleanup): finalize executions after job failure (#11408) --- app/Jobs/DockerCleanupJob.php | 34 ++++++++++++++++++++++++++ tests/Feature/DockerCleanupJobTest.php | 22 +++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 16f3d88ad9..5a7627e0a9 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -155,4 +155,38 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue } } } + + public function failed(?\Throwable $exception): void + { + $execution = DockerCleanupExecution::query() + ->where('server_id', $this->server->id) + ->where('status', 'running') + ->whereNull('finished_at') + ->latest('id') + ->first(); + + if (! $execution) { + return; + } + + $message = $exception?->getMessage() ?? 'Docker cleanup job failed without an exception.'; + + $updated = DockerCleanupExecution::query() + ->whereKey($execution->id) + ->where('status', 'running') + ->whereNull('finished_at') + ->update([ + 'status' => 'failed', + 'message' => $message, + 'finished_at' => Carbon::now()->toImmutable(), + ]); + + if ($updated === 0) { + return; + } + + $execution->refresh(); + event(new DockerCleanupDone($execution)); + $this->server->team?->notify(new DockerCleanupFailed($this->server, 'Docker cleanup job failed with the following error: '.$message)); + } } diff --git a/tests/Feature/DockerCleanupJobTest.php b/tests/Feature/DockerCleanupJobTest.php index fa052f6c23..081b49a520 100644 --- a/tests/Feature/DockerCleanupJobTest.php +++ b/tests/Feature/DockerCleanupJobTest.php @@ -64,3 +64,25 @@ it('creates a failed execution record when server is force disabled', function ( ->and($execution->status)->toBe('failed') ->and($execution->message)->toContain('not functional'); }); + +it('finishes the latest running execution when the job fails after a timeout', function () { + $user = User::factory()->create(); + $team = $user->teams()->first(); + $server = Server::factory()->create(['team_id' => $team->id]); + + $olderExecution = DockerCleanupExecution::create([ + 'server_id' => $server->id, + ]); + $timedOutExecution = DockerCleanupExecution::create([ + 'server_id' => $server->id, + ]); + + $job = new DockerCleanupJob($server); + $job->failed(new RuntimeException('Docker cleanup job has timed out.')); + + expect($timedOutExecution->refresh()->status)->toBe('failed') + ->and($timedOutExecution->message)->toBe('Docker cleanup job has timed out.') + ->and($timedOutExecution->finished_at)->not->toBeNull() + ->and($olderExecution->refresh()->status)->toBe('running') + ->and($olderExecution->finished_at)->toBeNull(); +});