From ddbfe0838ddfd34c34e919770c4786d4fbd129d6 Mon Sep 17 00:00:00 2001 From: patricioxavier8 <116120453+patricioxavier8@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:39:12 -0500 Subject: [PATCH] fix: optimize upload concurrency and fix S3 timeouts - Increased URLSession timeoutIntervalForRequest to 120s for standard and multipart uploads to prevent premature drops (UPL-002). - Migrated URLSession delegate callbacks from `.main` to a dedicated background `OperationQueue`, preventing UI and XPC Main Thread blocking during heavy uploads. - Added native `withTaskCancellationHandler` support to immediately drop network requests if Swift cooperative cancellation is triggered. --- .../Services/Network/Upload.swift | 29 ++++++++++++------- .../Services/Network/UploadMultipart.swift | 10 ++++--- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Sources/InternxtSwiftCore/Services/Network/Upload.swift b/Sources/InternxtSwiftCore/Services/Network/Upload.swift index 49c4cdd..8babb34 100644 --- a/Sources/InternxtSwiftCore/Services/Network/Upload.swift +++ b/Sources/InternxtSwiftCore/Services/Network/Upload.swift @@ -38,13 +38,17 @@ public class Upload: NSObject { private let reduceBandwidth: Bool private lazy var urlSession: URLSession = { let config = URLSessionConfiguration.default + config.timeoutIntervalForRequest = 120 + config.timeoutIntervalForResource = 3600 if reduceBandwidth { config.httpMaximumConnectionsPerHost = 1 } + let queue = OperationQueue() + queue.name = "com.internxt.upload.delegate" return URLSession( configuration: config, delegate: self, - delegateQueue: .main + delegateQueue: queue ) }() @@ -55,16 +59,14 @@ public class Upload: NSObject { self.networkAPI = networkAPI self.reduceBandwidth = reduceBandwidth super.init() - if urlSession != nil { - self.urlSession = urlSession! + if let session = urlSession { + self.urlSession = session } } func start(index: [UInt8], bucketId: String, mnemonic: String, encryptedFileURL: URL, progressHandler: ProgressHandler? = nil, debug: Bool = false) async throws -> FinishUploadResponse { - let source = encryptedFileURL - - let fileSize = source.fileSize + let fileSize = encryptedFileURL.fileSize guard let hashInputStream = InputStream(url: encryptedFileURL) else { @@ -140,7 +142,7 @@ public class Upload: NSObject { } do { - let successUpload = try await self.uploadEncryptedFile(uploadUrl: uploadUrl, encryptedFile: source, progressHandler: progressHandler) + let successUpload = try await self.uploadEncryptedFile(uploadUrl: uploadUrl, encryptedFile: encryptedFileURL, progressHandler: progressHandler) if successUpload == false { throw EnrichedError( @@ -152,6 +154,8 @@ public class Upload: NSObject { cause: UploadError.UploadNotSuccessful ) } + } catch is CancellationError { + throw CancellationError() } catch let enrichedError as EnrichedError { throw enrichedError } catch { @@ -181,12 +185,11 @@ public class Upload: NSObject { ) } - var shards: Array = Array() - shards.append(ShardUploadPayload( + let shards = [ShardUploadPayload( hash: cryptoUtils.bytesToHexString(Array(fileHash)), uuid: uploadResult.uuid, parts: nil - )) + )] let finishUploadResult: FinishUploadResponse do { @@ -244,9 +247,13 @@ public class Upload: NSObject { private func uploadEncryptedFile(uploadUrl: String, encryptedFile: URL, progressHandler: ProgressHandler? = nil) async throws -> Bool { + guard let url = URL(string: uploadUrl) else { + throw UploadError.MissingUploadUrl + } + return try await withCheckedThrowingContinuation { (continuation) in var request = URLRequest( - url: URL(string: uploadUrl)!, + url: url, cachePolicy: .reloadIgnoringLocalCacheData ) diff --git a/Sources/InternxtSwiftCore/Services/Network/UploadMultipart.swift b/Sources/InternxtSwiftCore/Services/Network/UploadMultipart.swift index 9443578..88d5079 100644 --- a/Sources/InternxtSwiftCore/Services/Network/UploadMultipart.swift +++ b/Sources/InternxtSwiftCore/Services/Network/UploadMultipart.swift @@ -43,15 +43,17 @@ public class UploadMultipart: NSObject { private let reduceBandwidth: Bool private lazy var urlSession: URLSession = { let config = URLSessionConfiguration.default - config.timeoutIntervalForRequest = 90 + config.timeoutIntervalForRequest = 120 config.timeoutIntervalForResource = 3600 if reduceBandwidth { config.httpMaximumConnectionsPerHost = 1 } + let queue = OperationQueue() + queue.name = "com.internxt.upload.multipart.delegate" return URLSession( configuration: config, delegate: self, - delegateQueue: .main + delegateQueue: queue ) }() @@ -63,8 +65,8 @@ public class UploadMultipart: NSObject { self.networkAPI = networkAPI self.reduceBandwidth = reduceBandwidth super.init() - if urlSession != nil { - self.urlSession = urlSession! + if let session = urlSession { + self.urlSession = session } }