From 1ca4b08b480fbbec6a018e787a3aea1d71f2e9cc Mon Sep 17 00:00:00 2001 From: Pascal Zarrad Date: Sun, 25 Sep 2022 22:38:49 +0200 Subject: [PATCH] backups: add S3 part size configuration (#4382) --- .../Backups/BackupRemoteUploadController.php | 32 +++++++++++++++++-- config/backups.php | 5 +++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php index 042486c24..515003ac9 100644 --- a/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php +++ b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php @@ -15,7 +15,7 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class BackupRemoteUploadController extends Controller { - public const PART_SIZE = 5 * 1024 * 1024 * 1024; + public const DEFAULT_MAX_PART_SIZE = 5 * 1024 * 1024 * 1024; /** * @var \Pterodactyl\Repositories\Eloquent\BackupRepository @@ -89,9 +89,12 @@ class BackupRemoteUploadController extends Controller // the other presigned urls. $params['UploadId'] = $result->get('UploadId'); + // Retrieve configured part size + $maxPartSize = $this->getConfiguredMaxPartSize(); + // Create as many UploadPart presigned urls as needed $parts = []; - for ($i = 0; $i < ($size / self::PART_SIZE); ++$i) { + for ($i = 0; $i < ($size / $maxPartSize); ++$i) { $parts[] = $client->createPresignedRequest( $client->getCommand('UploadPart', array_merge($params, ['PartNumber' => $i + 1])), $expires @@ -103,7 +106,30 @@ class BackupRemoteUploadController extends Controller return new JsonResponse([ 'parts' => $parts, - 'part_size' => self::PART_SIZE, + 'part_size' => $maxPartSize, ]); } + + /** + * Get the configured maximum size of a single part in the multipart uplaod. + * + * The function tries to retrieve a configured value from the configuration. + * If no value is specified, a fallback value will be used. + * + * Note if the received config cannot be converted to int (0), is zero or is negative, + * the fallback value will be used too. + * + * The fallback value is {@see BackupRemoteUploadController::DEFAULT_MAX_PART_SIZE}. + * + * @return int + */ + private function getConfiguredMaxPartSize() + { + $maxPartSize = (int) config('backups.max_part_size', self::DEFAULT_MAX_PART_SIZE); + if ($maxPartSize <= 0) { + $maxPartSize = self::DEFAULT_MAX_PART_SIZE; + } + + return $maxPartSize; + } } diff --git a/config/backups.php b/config/backups.php index 21bddfe7b..3e6ecc6ec 100644 --- a/config/backups.php +++ b/config/backups.php @@ -12,6 +12,11 @@ return [ // uses to upload backups to S3 storage. Value is in minutes, so this would default to an hour. 'presigned_url_lifespan' => env('BACKUP_PRESIGNED_URL_LIFESPAN', 60), + // This value defines the maximal size of a single part for the S3 multipart upload during backups + // The maximal part size must be given in bytes. The default value is 5GB. + // Note that 5GB is the maximum for a single part when using AWS S3. + 'max_part_size' => env('BACKUP_MAX_PART_SIZE', 5 * 1024 * 1024 * 1024), + // The time to wait before automatically failing a backup, time is in minutes and defaults // to 6 hours. To disable this feature, set the value to `0`. 'prune_age' => env('BACKUP_PRUNE_AGE', 360),