From b0b160e8eeb1b33d21e033b2cf12d78aeea25ca3 Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Mon, 10 Aug 2026 08:40:36 +0200 Subject: [PATCH 1/2] fix(uploads): lower multipart upload threshold to 10 MB --- src/services/app-bundle-files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/app-bundle-files.ts b/src/services/app-bundle-files.ts index 140b659..b5b57e9 100644 --- a/src/services/app-bundle-files.ts +++ b/src/services/app-bundle-files.ts @@ -23,7 +23,7 @@ class AppBundleFilesServiceImpl implements AppBundleFilesService { onProgress?: (currentPart: number, totalParts: number) => void, ): Promise { const sizeInBytes = dto.buffer.byteLength; - const useMultipartUpload = sizeInBytes >= 50 * 1024 * 1024; // 50 MB + const useMultipartUpload = sizeInBytes >= 10 * 1024 * 1024; // 10 MB const formData = new FormData(); formData.append('checksum', dto.checksum); if (!useMultipartUpload) { From da349c4cb1652f61a19de54ab66fdce32e7f7ff1 Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Mon, 10 Aug 2026 08:49:28 +0200 Subject: [PATCH 2/2] refactor(uploads): extract upload threshold and part size into constants --- src/config/consts.ts | 2 ++ src/services/app-build-sources.ts | 9 ++++----- src/services/app-bundle-files.ts | 15 +++++++++------ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/config/consts.ts b/src/config/consts.ts index 4bf6331..1b9e505 100644 --- a/src/config/consts.ts +++ b/src/config/consts.ts @@ -3,3 +3,5 @@ export const DEFAULT_CONSOLE_BASE_URL = 'https://console.cloud.capawesome.io'; export const MANIFEST_JSON_FILE_NAME = 'capawesome-live-update-manifest.json'; // Do NOT change this! export const MAX_CONCURRENT_FILE_UPLOADS = 20; export const MAX_CONCURRENT_PART_UPLOADS = 4; +export const MULTIPART_UPLOAD_THRESHOLD_IN_BYTES = 10 * 1024 * 1024; // 10 MB +export const UPLOAD_PART_SIZE_IN_BYTES = 10 * 1024 * 1024; // 10 MB. 5 MB is the minimum part size except for the last part. diff --git a/src/services/app-build-sources.ts b/src/services/app-build-sources.ts index a023faf..be65f81 100644 --- a/src/services/app-build-sources.ts +++ b/src/services/app-build-sources.ts @@ -1,4 +1,4 @@ -import { MAX_CONCURRENT_PART_UPLOADS } from '@/config/index.js'; +import { MAX_CONCURRENT_PART_UPLOADS, UPLOAD_PART_SIZE_IN_BYTES } from '@/config/index.js'; import authorizationService from '@/services/authorization-service.js'; import { AppBuildSourceDto, CreateAppBuildSourceDto } from '@/types/app-build-source.js'; import httpClient, { HttpClient } from '@/utils/http-client.js'; @@ -110,8 +110,7 @@ class AppBuildSourcesServiceImpl implements AppBuildSourcesService { onProgress?: (currentPart: number, totalParts: number) => void, ): Promise { const uploadedParts: AppBuildSourceUploadPartDto[] = []; - const partSize = 10 * 1024 * 1024; // 10 MB - const totalParts = Math.ceil(dto.buffer.byteLength / partSize); + const totalParts = Math.ceil(dto.buffer.byteLength / UPLOAD_PART_SIZE_IN_BYTES); let partNumber = 0; const uploadNextPart = async () => { if (partNumber >= totalParts) { @@ -119,8 +118,8 @@ class AppBuildSourcesServiceImpl implements AppBuildSourcesService { } partNumber++; onProgress?.(partNumber, totalParts); - const start = (partNumber - 1) * partSize; - const end = Math.min(start + partSize, dto.buffer.byteLength); + const start = (partNumber - 1) * UPLOAD_PART_SIZE_IN_BYTES; + const end = Math.min(start + UPLOAD_PART_SIZE_IN_BYTES, dto.buffer.byteLength); const partBuffer = dto.buffer.subarray(start, end); const uploadedPart = await this.createUploadPart({ diff --git a/src/services/app-bundle-files.ts b/src/services/app-bundle-files.ts index b5b57e9..6bebba1 100644 --- a/src/services/app-bundle-files.ts +++ b/src/services/app-bundle-files.ts @@ -1,4 +1,8 @@ -import { MAX_CONCURRENT_PART_UPLOADS } from '@/config/index.js'; +import { + MAX_CONCURRENT_PART_UPLOADS, + MULTIPART_UPLOAD_THRESHOLD_IN_BYTES, + UPLOAD_PART_SIZE_IN_BYTES, +} from '@/config/index.js'; import authorizationService from '@/services/authorization-service.js'; import { AppBundleFileDto, CreateAppBundleFileDto } from '@/types/app-bundle-file.js'; import httpClient, { HttpClient } from '@/utils/http-client.js'; @@ -23,7 +27,7 @@ class AppBundleFilesServiceImpl implements AppBundleFilesService { onProgress?: (currentPart: number, totalParts: number) => void, ): Promise { const sizeInBytes = dto.buffer.byteLength; - const useMultipartUpload = sizeInBytes >= 10 * 1024 * 1024; // 10 MB + const useMultipartUpload = sizeInBytes >= MULTIPART_UPLOAD_THRESHOLD_IN_BYTES; const formData = new FormData(); formData.append('checksum', dto.checksum); if (!useMultipartUpload) { @@ -117,8 +121,7 @@ class AppBundleFilesServiceImpl implements AppBundleFilesService { onProgress?: (currentPart: number, totalParts: number) => void, ): Promise { const uploadedParts: AppBundleFileUploadPartDto[] = []; - const partSize = 10 * 1024 * 1024; // 10 MB. 5 MB is the minimum part size except for the last part. - const totalParts = Math.ceil(dto.buffer.byteLength / partSize); + const totalParts = Math.ceil(dto.buffer.byteLength / UPLOAD_PART_SIZE_IN_BYTES); let partNumber = 0; const uploadNextPart = async () => { if (partNumber >= totalParts) { @@ -126,8 +129,8 @@ class AppBundleFilesServiceImpl implements AppBundleFilesService { } partNumber++; onProgress?.(partNumber, totalParts); - const start = (partNumber - 1) * partSize; - const end = Math.min(start + partSize, dto.buffer.byteLength); + const start = (partNumber - 1) * UPLOAD_PART_SIZE_IN_BYTES; + const end = Math.min(start + UPLOAD_PART_SIZE_IN_BYTES, dto.buffer.byteLength); const partBuffer = dto.buffer.subarray(start, end); const uploadedPart = await this.createUploadPart({