fix(backups): large database backups are not working (#6217)

This commit is contained in:
🏔️ Peak
2025-07-18 15:47:14 +02:00
committed by GitHub
parent ed6e58f7ee
commit e5e1bdcd4d
4 changed files with 52 additions and 5 deletions

View File

@@ -23,6 +23,8 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Throwable;
use Visus\Cuid2\Cuid2;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -60,9 +62,16 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
public ?S3Storage $s3 = null; public ?S3Storage $s3 = null;
public $timeout = 3600;
public string $backup_log_uuid;
public function __construct(public ScheduledDatabaseBackup $backup) public function __construct(public ScheduledDatabaseBackup $backup)
{ {
$this->onQueue('high'); $this->onQueue('high');
$this->timeout = $backup->timeout;
$this->backup_log_uuid = (string) new Cuid2;
} }
public function handle(): void public function handle(): void
@@ -219,12 +228,8 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->mongo_root_username = str($rootUsername)->after('MONGO_INITDB_ROOT_USERNAME=')->value(); $this->mongo_root_username = str($rootUsername)->after('MONGO_INITDB_ROOT_USERNAME=')->value();
} }
} }
\Log::info('MongoDB credentials extracted from environment', [
'has_username' => filled($this->mongo_root_username),
'has_password' => filled($this->mongo_root_password),
]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
\Log::warning('Failed to extract MongoDB environment variables', ['error' => $e->getMessage()]);
// Continue without env vars - will be handled in backup_standalone_mongodb method // Continue without env vars - will be handled in backup_standalone_mongodb method
} }
} }
@@ -288,6 +293,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} }
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'uuid' => $this->backup_log_uuid,
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
@@ -307,6 +313,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz'; $this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz';
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'uuid' => $this->backup_log_uuid,
'database_name' => $databaseName, 'database_name' => $databaseName,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
@@ -319,6 +326,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} }
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'uuid' => $this->backup_log_uuid,
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
@@ -331,6 +339,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} }
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'uuid' => $this->backup_log_uuid,
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
@@ -574,4 +583,18 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
return "{$helperImage}:{$latestVersion}"; return "{$helperImage}:{$latestVersion}";
} }
public function failed(?Throwable $exception): void
{
$log = ScheduledDatabaseBackupExecution::where('uuid', $this->backup_log_uuid)->first();
if ($log) {
$log->update([
'status' => 'failed',
'message' => 'Job failed: '.$exception->getMessage(),
'size' => 0,
'filename' => null,
]);
}
}
} }

View File

@@ -73,6 +73,9 @@ class BackupEdit extends Component
#[Validate(['required', 'boolean'])] #[Validate(['required', 'boolean'])]
public bool $dumpAll = false; public bool $dumpAll = false;
#[Validate(['required', 'int', 'min:1', 'max:36000'])]
public int $timeout = 3600;
public function mount() public function mount()
{ {
try { try {
@@ -98,6 +101,7 @@ class BackupEdit extends Component
$this->backup->s3_storage_id = $this->s3StorageId; $this->backup->s3_storage_id = $this->s3StorageId;
$this->backup->databases_to_backup = $this->databasesToBackup; $this->backup->databases_to_backup = $this->databasesToBackup;
$this->backup->dump_all = $this->dumpAll; $this->backup->dump_all = $this->dumpAll;
$this->backup->timeout = $this->timeout;
$this->customValidate(); $this->customValidate();
$this->backup->save(); $this->backup->save();
} else { } else {
@@ -114,6 +118,7 @@ class BackupEdit extends Component
$this->s3StorageId = $this->backup->s3_storage_id; $this->s3StorageId = $this->backup->s3_storage_id;
$this->databasesToBackup = $this->backup->databases_to_backup; $this->databasesToBackup = $this->backup->databases_to_backup;
$this->dumpAll = $this->backup->dump_all; $this->dumpAll = $this->backup->dump_all;
$this->timeout = $this->backup->timeout;
} }
} }

View File

@@ -0,0 +1,18 @@
<?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_backups', function (Blueprint $table) {
$table->integer('timeout')->default(3600);
});
}
};

View File

@@ -77,6 +77,7 @@
<x-forms.input label="Frequency" id="frequency" /> <x-forms.input label="Frequency" id="frequency" />
<x-forms.input label="Timezone" id="timezone" disabled <x-forms.input label="Timezone" id="timezone" disabled
helper="The timezone of the server where the backup is scheduled to run (if not set, the instance timezone will be used)" /> helper="The timezone of the server where the backup is scheduled to run (if not set, the instance timezone will be used)" />
<x-forms.input label="Timeout" id="timeout" helper="The timeout of the backup job in seconds."/>
</div> </div>
<h3 class="mt-6 mb-2 text-lg font-medium">Backup Retention Settings</h3> <h3 class="mt-6 mb-2 text-lg font-medium">Backup Retention Settings</h3>