fix(backups): set finished_at for each database of a backup run

In a backup of several databases, only the last execution got
finished_at.

- Set finished_at when each database backup succeeds or fails
This commit is contained in:
Andras Bacsai
2026-10-05 21:20:35 +02:00
parent 14523ed738
commit b794da0983
2 changed files with 42 additions and 0 deletions
+2
View File
@@ -464,6 +464,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
'size' => $size,
'filename' => null,
's3_uploaded' => null,
'finished_at' => Carbon::now()->toImmutable(),
]);
}
try {
@@ -512,6 +513,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
'size' => $size,
's3_uploaded' => $this->backup->save_s3 ? $this->s3_uploaded : null,
'local_storage_deleted' => $localStorageDeleted,
'finished_at' => Carbon::now()->toImmutable(),
]);
// Send appropriate notification (wrapped in try-catch so notification
@@ -73,3 +73,43 @@ it('keeps the successful execution of an earlier database when creating the next
->and($first->filename)->not->toBeNull()
->and($commands->contains(fn (string $command) => str_contains($command, 'rm ') && str_contains($command, $first->filename)))->toBeFalse();
});
it('finishes the execution of every database in a run with several databases', function () {
$team = Team::factory()->create();
$server = Server::factory()->create([
'team_id' => $team->id,
'private_key_id' => PrivateKey::factory()->create(['team_id' => $team->id])->id,
]);
$server->settings()->update(['is_reachable' => true, 'is_usable' => true]);
$destination = StandaloneDocker::where('server_id', $server->id)->firstOrFail();
$environment = Environment::factory()->create(['project_id' => Project::factory()->create(['team_id' => $team->id])->id]);
$database = create_standalone_postgresql($environment->id, $destination, ['status' => 'running:healthy']);
$backup = new ScheduledDatabaseBackup;
$backup->forceFill([
'enabled' => true,
'save_s3' => false,
'frequency' => 'daily',
'databases_to_backup' => 'first,second,third',
'database_id' => $database->id,
'database_type' => $database->getMorphClass(),
'team_id' => $team->id,
])->save();
Process::fake(function ($process) {
$command = is_array($process->command) ? implode(' ', $process->command) : $process->command;
if (str_contains($command, 'pg_dump') && str_contains($command, 'second')) {
return Process::result(errorOutput: 'pg_dump: database "second" does not exist', exitCode: 1);
}
return Process::result(output: str_contains($command, 'du -b') ? '128' : '');
});
(new DatabaseBackupJob($backup->fresh()))->handle();
$executions = ScheduledDatabaseBackupExecution::query()->orderBy('id')->get();
expect($executions->map->only(['database_name', 'status'])->all())->toBe([
['database_name' => 'first', 'status' => 'success'],
['database_name' => 'second', 'status' => 'failed'],
['database_name' => 'third', 'status' => 'success'],
])
->and($executions->whereNull('finished_at')->pluck('database_name')->all())->toBe([]);
});