Skip to content

Commit 4a744ef

Browse files
eofsbsneed
andauthored
Fix memory leak when processing locally stored events (#334)
* Version 1.5.10 * Use autorelease pools to release allocated memory on each iteration to avoid unnecessary memory consumption. This is necessary when working with Objective-C objects (e.g. NSData). https://stackoverflow.com/questions/25860942/is-it-necessary-to-use-autoreleasepool-in-a-swift-program/25880106#25880106 --------- Co-authored-by: Brandon Sneed <[email protected]>
1 parent 8145e9e commit 4a744ef

File tree

2 files changed

+46
-38
lines changed

2 files changed

+46
-38
lines changed

Sources/Segment/Plugins/SegmentDestination.swift

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -160,46 +160,54 @@ extension SegmentDestination {
160160
guard let analytics = self.analytics else { return }
161161
guard let httpClient = self.httpClient else { return }
162162

163-
guard let files = storage.dataStore.fetch()?.dataFiles else { return }
164-
165-
for url in files {
166-
// enter for this url we're going to kick off
167-
group.enter()
168-
analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)")
163+
// Cooperative release of allocated memory by URL instances (dataFiles).
164+
autoreleasepool {
165+
guard let files = storage.dataStore.fetch()?.dataFiles else { return }
169166

170-
// set up the task
171-
let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { [weak self] result in
172-
defer {
173-
group.leave()
174-
}
175-
guard let self else { return }
176-
switch result {
177-
case .success(_):
178-
storage.remove(data: [url])
179-
cleanupUploads()
167+
for url in files {
168+
// Use the autorelease pool to ensure that unnecessary memory allocations
169+
// are released after each iteration. If there is a large backlog of files
170+
// to iterate, the host applications may crash due to OOM issues.
171+
autoreleasepool {
172+
// enter for this url we're going to kick off
173+
group.enter()
174+
analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)")
180175

181-
// we don't want to retry events in a given batch when a 400
182-
// response for malformed JSON is returned
183-
case .failure(Segment.HTTPClientErrors.statusCode(code: 400)):
184-
storage.remove(data: [url])
185-
cleanupUploads()
186-
default:
187-
break
176+
// set up the task
177+
let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { [weak self] result in
178+
defer {
179+
group.leave()
180+
}
181+
guard let self else { return }
182+
switch result {
183+
case .success(_):
184+
storage.remove(data: [url])
185+
cleanupUploads()
186+
187+
// we don't want to retry events in a given batch when a 400
188+
// response for malformed JSON is returned
189+
case .failure(Segment.HTTPClientErrors.statusCode(code: 400)):
190+
storage.remove(data: [url])
191+
cleanupUploads()
192+
default:
193+
break
194+
}
195+
196+
analytics.log(message: "Processed: \(url.lastPathComponent)")
197+
// the upload we have here has just finished.
198+
// make sure it gets removed and it's cleanup() called rather
199+
// than waiting on the next flush to come around.
200+
cleanupUploads()
201+
}
202+
203+
// we have a legit upload in progress now, so add it to our list.
204+
if let upload = uploadTask {
205+
add(uploadTask: UploadTaskInfo(url: url, data: nil, task: upload))
206+
} else {
207+
// we couldn't get a task, so we need to leave the group or things will hang.
208+
group.leave()
209+
}
188210
}
189-
190-
analytics.log(message: "Processed: \(url.lastPathComponent)")
191-
// the upload we have here has just finished.
192-
// make sure it gets removed and it's cleanup() called rather
193-
// than waiting on the next flush to come around.
194-
cleanupUploads()
195-
}
196-
197-
// we have a legit upload in progress now, so add it to our list.
198-
if let upload = uploadTask {
199-
add(uploadTask: UploadTaskInfo(url: url, data: nil, task: upload))
200-
} else {
201-
// we couldn't get a task, so we need to leave the group or things will hang.
202-
group.leave()
203211
}
204212
}
205213
}

Sources/Segment/Version.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
// Use release.sh's automation.
1616

1717
// BREAKING.FEATURE.FIX
18-
internal let __segment_version = "1.5.9"
18+
internal let __segment_version = "1.5.10"

0 commit comments

Comments
 (0)