diff --git a/Sources/Segment/Plugins/SegmentDestination.swift b/Sources/Segment/Plugins/SegmentDestination.swift index 824b18d..41fdd42 100644 --- a/Sources/Segment/Plugins/SegmentDestination.swift +++ b/Sources/Segment/Plugins/SegmentDestination.swift @@ -160,46 +160,54 @@ extension SegmentDestination { guard let analytics = self.analytics else { return } guard let httpClient = self.httpClient else { return } - guard let files = storage.dataStore.fetch()?.dataFiles else { return } - - for url in files { - // enter for this url we're going to kick off - group.enter() - analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)") + // Cooperative release of allocated memory by URL instances (dataFiles). + autoreleasepool { + guard let files = storage.dataStore.fetch()?.dataFiles else { return } - // set up the task - let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { [weak self] result in - defer { - group.leave() - } - guard let self else { return } - switch result { - case .success(_): - storage.remove(data: [url]) - cleanupUploads() + for url in files { + // Use the autorelease pool to ensure that unnecessary memory allocations + // are released after each iteration. If there is a large backlog of files + // to iterate, the host applications may crash due to OOM issues. + autoreleasepool { + // enter for this url we're going to kick off + group.enter() + analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)") - // we don't want to retry events in a given batch when a 400 - // response for malformed JSON is returned - case .failure(Segment.HTTPClientErrors.statusCode(code: 400)): - storage.remove(data: [url]) - cleanupUploads() - default: - break + // set up the task + let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { [weak self] result in + defer { + group.leave() + } + guard let self else { return } + switch result { + case .success(_): + storage.remove(data: [url]) + cleanupUploads() + + // we don't want to retry events in a given batch when a 400 + // response for malformed JSON is returned + case .failure(Segment.HTTPClientErrors.statusCode(code: 400)): + storage.remove(data: [url]) + cleanupUploads() + default: + break + } + + analytics.log(message: "Processed: \(url.lastPathComponent)") + // the upload we have here has just finished. + // make sure it gets removed and it's cleanup() called rather + // than waiting on the next flush to come around. + cleanupUploads() + } + + // we have a legit upload in progress now, so add it to our list. + if let upload = uploadTask { + add(uploadTask: UploadTaskInfo(url: url, data: nil, task: upload)) + } else { + // we couldn't get a task, so we need to leave the group or things will hang. + group.leave() + } } - - analytics.log(message: "Processed: \(url.lastPathComponent)") - // the upload we have here has just finished. - // make sure it gets removed and it's cleanup() called rather - // than waiting on the next flush to come around. - cleanupUploads() - } - - // we have a legit upload in progress now, so add it to our list. - if let upload = uploadTask { - add(uploadTask: UploadTaskInfo(url: url, data: nil, task: upload)) - } else { - // we couldn't get a task, so we need to leave the group or things will hang. - group.leave() } } } diff --git a/Sources/Segment/Version.swift b/Sources/Segment/Version.swift index 81cff3b..f3cfc85 100644 --- a/Sources/Segment/Version.swift +++ b/Sources/Segment/Version.swift @@ -15,4 +15,4 @@ // Use release.sh's automation. // BREAKING.FEATURE.FIX -internal let __segment_version = "1.5.9" +internal let __segment_version = "1.5.10"