fix(backups): retention settings

- If you set a low local backup retention, for example 2 backups for local backup retention and 10 backups for S3, then the S3 backups were never deleted, not even after 10 days. This was because we check the file paths based on the backup executions table, and as soon as a backup was deleted locally, the execution was removed, which meant after 10 days for s3 there where no backups older then 10 days just the 2 local backups which is just wrong. Now we only delete a backup execution if it has been removed from both locations.
- Also added a nice little UI element to see where your backup is available.
This commit is contained in:
peaklabs-dev
2025-03-14 15:28:12 +01:00
parent b6cb32c037
commit ee93ccd8e7
3 changed files with 84 additions and 6 deletions

View File

@@ -233,15 +233,29 @@ function deleteEmptyBackupFolder($folderPath, Server $server): void
function removeOldBackups($backup): void
{
try {
$processedBackups = deleteOldBackupsLocally($backup);
if ($backup->save_s3) {
$processedBackups = $processedBackups->merge(deleteOldBackupsFromS3($backup));
if ($backup->executions) {
$localBackupsToDelete = deleteOldBackupsLocally($backup);
if ($localBackupsToDelete->isNotEmpty()) {
$backup->executions()
->whereIn('id', $localBackupsToDelete->pluck('id'))
->update(['local_storage_deleted' => true]);
}
}
if ($processedBackups->isNotEmpty()) {
$backup->executions()->whereIn('id', $processedBackups->pluck('id'))->delete();
if ($backup->save_s3 && $backup->executions) {
$s3BackupsToDelete = deleteOldBackupsFromS3($backup);
if ($s3BackupsToDelete->isNotEmpty()) {
$backup->executions()
->whereIn('id', $s3BackupsToDelete->pluck('id'))
->update(['s3_storage_deleted' => true]);
}
}
$backup->executions()
->where('local_storage_deleted', true)
->where('s3_storage_deleted', true)
->delete();
} catch (\Exception $e) {
throw $e;
}
@@ -255,6 +269,7 @@ function deleteOldBackupsLocally($backup): Collection
$successfulBackups = $backup->executions()
->where('status', 'success')
->where('local_storage_deleted', false)
->orderBy('created_at', 'desc')
->get();
@@ -338,6 +353,7 @@ function deleteOldBackupsFromS3($backup): Collection
$successfulBackups = $backup->executions()
->where('status', 'success')
->where('s3_storage_deleted', false)
->orderBy('created_at', 'desc')
->get();

View File

@@ -0,0 +1,19 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('scheduled_database_backup_executions', function (Blueprint $table) {
$table->boolean('local_storage_deleted')->default(false);
$table->boolean('s3_storage_deleted')->default(false);
});
}
};

View File

@@ -54,6 +54,49 @@
<div class="text-gray-600 dark:text-gray-400 text-sm">
Location: {{ data_get($execution, 'filename', 'N/A') }}
</div>
<div class="flex items-center gap-3 mt-2">
<div class="text-gray-600 dark:text-gray-400 text-sm">
Backup Availability:
</div>
<span @class([
'px-2 py-1 rounded text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 'local_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 'local_storage_deleted', false),
])>
<span class="flex items-center gap-1">
@if(!data_get($execution, 'local_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
</svg>
@endif
Local Storage
</span>
</span>
@if($backup->save_s3)
<span @class([
'px-2 py-1 rounded text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 's3_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 's3_storage_deleted', false),
])>
<span class="flex items-center gap-1">
@if(!data_get($execution, 's3_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
</svg>
@endif
S3 Storage
</span>
</span>
@endif
</div>
@if (data_get($execution, 'message'))
<div class="mt-2 p-2 bg-gray-100 dark:bg-coolgray-200 rounded">
<pre class="whitespace-pre-wrap text-sm">{{ data_get($execution, 'message') }}</pre>