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),