fix: storage retention setting

- feat: add storage retention to local storage as well
- fix: UI input for max storage now allows exact decimals so MB input is now also possible
- fix: Database column is now decimal instead of integer
- fix: variable naming of storage check no longer overwrites $backup - renamed it to $backupExecution
This commit is contained in:
peaklabs-dev
2025-01-13 19:49:12 +01:00
parent 3347eb3a1a
commit e9f691bf45
4 changed files with 54 additions and 9 deletions

View File

@@ -49,14 +49,17 @@ class BackupEdit extends Component
#[Validate(['required', 'integer'])]
public ?int $databaseBackupRetentionDaysLocally = 0;
#[Validate(['required', 'numeric', 'min:0'])]
public ?float $databaseBackupRetentionMaxStorageLocally = 0;
#[Validate(['required', 'integer'])]
public ?int $databaseBackupRetentionAmountS3 = 0;
#[Validate(['required', 'integer'])]
public ?int $databaseBackupRetentionDaysS3 = 0;
#[Validate(['required', 'integer'])]
public ?int $databaseBackupRetentionMaxStorageS3 = 0;
#[Validate(['required', 'numeric', 'min:0'])]
public ?float $databaseBackupRetentionMaxStorageS3 = 0;
#[Validate(['required', 'boolean'])]
public bool $saveS3 = false;
@@ -87,6 +90,7 @@ class BackupEdit extends Component
$this->backup->frequency = $this->frequency;
$this->backup->database_backup_retention_amount_locally = $this->databaseBackupRetentionAmountLocally;
$this->backup->database_backup_retention_days_locally = $this->databaseBackupRetentionDaysLocally;
$this->backup->database_backup_retention_max_storage_locally = $this->databaseBackupRetentionMaxStorageLocally;
$this->backup->database_backup_retention_amount_s3 = $this->databaseBackupRetentionAmountS3;
$this->backup->database_backup_retention_days_s3 = $this->databaseBackupRetentionDaysS3;
$this->backup->database_backup_retention_max_storage_s3 = $this->databaseBackupRetentionMaxStorageS3;
@@ -102,6 +106,7 @@ class BackupEdit extends Component
$this->timezone = data_get($this->backup->server(), 'settings.server_timezone', 'Instance timezone');
$this->databaseBackupRetentionAmountLocally = $this->backup->database_backup_retention_amount_locally;
$this->databaseBackupRetentionDaysLocally = $this->backup->database_backup_retention_days_locally;
$this->databaseBackupRetentionMaxStorageLocally = $this->backup->database_backup_retention_max_storage_locally;
$this->databaseBackupRetentionAmountS3 = $this->backup->database_backup_retention_amount_s3;
$this->databaseBackupRetentionDaysS3 = $this->backup->database_backup_retention_days_s3;
$this->databaseBackupRetentionMaxStorageS3 = $this->backup->database_backup_retention_max_storage_s3;

View File

@@ -263,8 +263,9 @@ function deleteOldBackupsLocally($backup): Collection
$retentionAmount = $backup->database_backup_retention_amount_locally;
$retentionDays = $backup->database_backup_retention_days_locally;
$maxStorageGB = $backup->database_backup_retention_max_storage_locally;
if ($retentionAmount === 0 && $retentionDays === 0) {
if ($retentionAmount === 0 && $retentionDays === 0 && $maxStorageGB === 0) {
return collect();
}
@@ -281,6 +282,26 @@ function deleteOldBackupsLocally($backup): Collection
$backupsToDelete = $backupsToDelete->merge($oldBackups);
}
if ($maxStorageGB > 0) {
$maxStorageBytes = $maxStorageGB * pow(1024, 3);
$totalSize = 0;
$backupsOverLimit = collect();
$backupsToCheck = $successfulBackups->skip(1);
foreach ($backupsToCheck as $backupExecution) {
$totalSize += (int) $backupExecution->size;
if ($totalSize > $maxStorageBytes) {
$backupsOverLimit = $successfulBackups->filter(
fn ($b) => $b->created_at->utc() <= $backupExecution->created_at->utc()
)->skip(1);
break;
}
}
$backupsToDelete = $backupsToDelete->merge($backupsOverLimit);
}
$backupsToDelete = $backupsToDelete->unique('id');
$processedBackups = collect();
@@ -345,14 +366,18 @@ function deleteOldBackupsFromS3($backup): Collection
}
if ($maxStorageGB > 0) {
$maxStorageBytes = $maxStorageGB * 1024 * 1024 * 1024;
$maxStorageBytes = $maxStorageGB * pow(1024, 3);
$totalSize = 0;
$backupsOverLimit = collect();
foreach ($successfulBackups as $backup) {
$totalSize += (int) $backup->size;
$backupsToCheck = $successfulBackups->skip(1);
foreach ($backupsToCheck as $backupExecution) {
$totalSize += (int) $backupExecution->size;
if ($totalSize > $maxStorageBytes) {
$backupsOverLimit = $successfulBackups->filter(fn ($b) => $b->created_at->utc() <= $backup->created_at->utc());
$backupsOverLimit = $successfulBackups->filter(
fn ($b) => $b->created_at->utc() <= $backupExecution->created_at->utc()
)->skip(1);
break;
}
}

View File

@@ -12,10 +12,11 @@ return new class extends Migration
$table->renameColumn('number_of_backups_locally', 'database_backup_retention_amount_locally');
$table->integer('database_backup_retention_amount_locally')->default(0)->nullable(false)->change();
$table->integer('database_backup_retention_days_locally')->default(0)->nullable(false);
$table->decimal('database_backup_retention_max_storage_locally', 17, 7)->default(0)->nullable(false);
$table->integer('database_backup_retention_amount_s3')->default(0)->nullable(false);
$table->integer('database_backup_retention_days_s3')->default(0)->nullable(false);
$table->integer('database_backup_retention_max_storage_s3')->default(0)->nullable(false);
$table->decimal('database_backup_retention_max_storage_s3', 17, 7)->default(0)->nullable(false);
});
}
@@ -25,6 +26,7 @@ return new class extends Migration
$table->renameColumn('database_backup_retention_amount_locally', 'number_of_backups_locally')->nullable(true)->change();
$table->dropColumn([
'database_backup_retention_days_locally',
'database_backup_retention_max_storage_locally',
'database_backup_retention_amount_s3',
'database_backup_retention_days_s3',
'database_backup_retention_max_storage_s3',

View File

@@ -75,6 +75,13 @@
<div class="flex gap-4">
<x-forms.input label="Number of backups to keep" id="databaseBackupRetentionAmountLocally" type="number" min="0" helper="Keeps only the specified number of most recent backups on the server. Set to 0 for unlimited backups." />
<x-forms.input label="Days to keep backups" id="databaseBackupRetentionDaysLocally" type="number" min="0" helper="Automatically removes backups older than the specified number of days. Set to 0 for no time limit." />
<x-forms.input
label="Maximum storage (GB)"
id="databaseBackupRetentionMaxStorageLocally"
type="number"
min="0"
step="0.0000001"
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.001 for 1MB). Set to 0 for unlimited storage." />
</div>
</div>
@@ -84,7 +91,13 @@
<div class="flex gap-4">
<x-forms.input label="Number of backups to keep" id="databaseBackupRetentionAmountS3" type="number" min="0" helper="Keeps only the specified number of most recent backups on S3 storage. Set to 0 for unlimited backups." />
<x-forms.input label="Days to keep backups" id="databaseBackupRetentionDaysS3" type="number" min="0" helper="Automatically removes S3 backups older than the specified number of days. Set to 0 for no time limit." />
<x-forms.input label="Maximum storage (GB)" id="databaseBackupRetentionMaxStorageS3" type="number" min="0" helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Set to 0 for unlimited storage." />
<x-forms.input
label="Maximum storage (GB)"
id="databaseBackupRetentionMaxStorageS3"
type="number"
min="0"
step="0.0000001"
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.5 for 500MB). Set to 0 for unlimited storage." />
</div>
</div>
@endif