Skip to content

Commit ebb6bf9

Browse files
Consolidate StorageService persistence queues (#502)
Consolidate all StorageService persistence queues to prevent concurrency issues.
1 parent 97854e9 commit ebb6bf9

3 files changed

Lines changed: 26 additions & 31 deletions

File tree

Sources/Statsig/Storage/StorageService.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ final class StorageService {
55
private static let servicesLock = NSLock()
66
private static var servicesBySDKKey: [String: StorageService] = [:]
77

8+
static let persistenceQueue = DispatchQueue(
9+
label: "com.statsig.persistence", attributes: .concurrent)
10+
811
let sdkKey: String
912
let userPayload: UserPayloadStore
1013

Sources/Statsig/Storage/StoreItems/UserPayloadIndex.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ final class UserPayloadIndexStore {
7777
let sdkKey: String
7878
let indexFileKey: [String]
7979
private let storageAdapter: StorageAdapter
80-
private let indexPersistenceQueue: DispatchQueue
8180

8281
private let indexLock = NSLock()
8382
private var index: UserPayloadIndex
@@ -90,11 +89,6 @@ final class UserPayloadIndexStore {
9089
self.indexFileKey = [sdkKey, USER_PAYLOAD_DIRNAME, USER_PAYLOAD_INDEX_FILENAME]
9190
self.storageAdapter = storageAdapter
9291
self.index = Self.readIndex(sdkKey: sdkKey, storageAdapter: storageAdapter).index
93-
self.indexPersistenceQueue = DispatchQueue(
94-
label:
95-
"com.statsig.userPayload.index.persistence.\(String(sdkKey.dropFirst(7).prefix(4)))",
96-
qos: .utility
97-
)
9892
}
9993

10094
// MARK: Cache Key Mapping
@@ -184,7 +178,7 @@ final class UserPayloadIndexStore {
184178
}
185179
let storageAdapter = self.storageAdapter
186180
let indexFileKey = self.indexFileKey
187-
indexPersistenceQueue.async {
181+
StorageService.persistenceQueue.async(flags: .barrier) {
188182
storageAdapter.write(indexData, indexFileKey, options: .createFolderIfNeeded)
189183
}
190184
}
@@ -198,7 +192,10 @@ final class UserPayloadIndexStore {
198192
let key = UserPayloadStore.sdkDirectoryKey(sdkKey: sdkKey) + [USER_PAYLOAD_INDEX_FILENAME]
199193

200194
let data: Data
201-
switch storageAdapter.read(key) {
195+
let readResult = StorageService.persistenceQueue.sync {
196+
storageAdapter.read(key)
197+
}
198+
switch readResult {
202199
case .data(let readData):
203200
data = readData
204201
case .notFound:
@@ -213,15 +210,13 @@ final class UserPayloadIndexStore {
213210
return (decoded, true)
214211
}
215212

216-
// FIXME: writeForMigration uses a different queue than persistIndexNow.
217213
public static func writeForMigration(
218214
key: [String],
219215
index: UserPayloadIndex,
220-
storageAdapter: StorageAdapter,
221-
persistenceQueue: DispatchQueue
216+
storageAdapter: StorageAdapter
222217
) {
223218
guard let data = index.encode(), !key.isEmpty else { return }
224-
persistenceQueue.async {
219+
StorageService.persistenceQueue.async(flags: .barrier) {
225220
storageAdapter.write(data, key)
226221
}
227222
}

Sources/Statsig/Storage/StoreItems/UserPayloadStore.swift

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ final class UserPayloadStore {
1212

1313
private static let storesLock = NSLock()
1414
private static var storesBySDKKey: [String: UserPayloadStore] = [:]
15-
private static var migrationPersistenceQueue = DispatchQueue(
16-
label: "com.statsig.userPayload.persistence.migration",
17-
qos: .utility,
18-
attributes: .concurrent
19-
)
2015

2116
static func forSDKKey(
2217
_ sdkKey: String,
@@ -42,7 +37,6 @@ final class UserPayloadStore {
4237
let sdkKey: String
4338
let storageAdapter: StorageAdapter
4439

45-
private let persistenceQueue: DispatchQueue
4640
private let evictionQueue: DispatchQueue
4741
private let indexStore: UserPayloadIndexStore
4842
private let directoryKey: [String]
@@ -54,9 +48,6 @@ final class UserPayloadStore {
5448
self.storageAdapter = storageAdapter
5549
self.directoryKey = Self.sdkDirectoryKey(sdkKey: sdkKey)
5650
let sdkKeyPrefix = String(sdkKey.dropFirst(7).prefix(4))
57-
self.persistenceQueue = DispatchQueue(
58-
label: "com.statsig.userPayload.persistence.\(sdkKeyPrefix)",
59-
qos: .utility)
6051
self.evictionQueue = DispatchQueue(
6152
label: "com.statsig.userPayload.eviction.\(sdkKeyPrefix)",
6253
qos: .utility)
@@ -110,7 +101,7 @@ final class UserPayloadStore {
110101
)
111102

112103
// Persist payload
113-
persistenceQueue.async {
104+
StorageService.persistenceQueue.async(flags: .barrier) {
114105
storageAdapter.write(data, payloadKey, options: .createFolderIfNeeded)
115106
}
116107

@@ -133,7 +124,7 @@ final class UserPayloadStore {
133124
return
134125
}
135126

136-
migrationPersistenceQueue.async {
127+
StorageService.persistenceQueue.async(flags: .barrier) {
137128
storageAdapter.write(data, key, options: [.withoutOverwriting])
138129
}
139130
}
@@ -242,7 +233,10 @@ final class UserPayloadStore {
242233
}
243234

244235
let data: Data
245-
switch storageAdapter.read(key) {
236+
let readResult = StorageService.persistenceQueue.sync {
237+
storageAdapter.read(key)
238+
}
239+
switch readResult {
246240
case .data(let readData):
247241
data = readData
248242
case .notFound, .error:
@@ -266,7 +260,7 @@ final class UserPayloadStore {
266260
payloadCacheByFilename.removeValue(forKey: filename(for: key))
267261

268262
// TODO: Handle errors
269-
persistenceQueue.async {
263+
StorageService.persistenceQueue.async(flags: .barrier) {
270264
storageAdapter.remove(payloadKey)
271265
}
272266
}
@@ -321,7 +315,9 @@ final class UserPayloadStore {
321315
}
322316
.prefix(MAX_CACHED_USER_PAYLOADS_PER_KEY)
323317
let sdkDirectoryKey = Self.sdkDirectoryKey(sdkKey: sdkKey)
324-
storageAdapter.createFolderIfNeeded(sdkDirectoryKey)
318+
StorageService.persistenceQueue.async(flags: .barrier) {
319+
storageAdapter.createFolderIfNeeded(sdkDirectoryKey)
320+
}
325321

326322
for entry in selectedEntries {
327323
index.entries[entry.fullUserHash] = IndexEntry(
@@ -339,8 +335,7 @@ final class UserPayloadStore {
339335
UserPayloadIndexStore.writeForMigration(
340336
key: sdkDirectoryKey + [USER_PAYLOAD_INDEX_FILENAME],
341337
index: index,
342-
storageAdapter: storageAdapter,
343-
persistenceQueue: migrationPersistenceQueue
338+
storageAdapter: storageAdapter
344339
)
345340
}
346341

@@ -353,7 +348,9 @@ final class UserPayloadStore {
353348
.prefix(MAX_CACHED_USER_PAYLOADS_PER_KEY)
354349

355350
if selectedLegacyEntries.count > 0 {
356-
storageAdapter.createFolderIfNeeded(LEGACY_DIRECTORY_KEY)
351+
StorageService.persistenceQueue.async(flags: .barrier) {
352+
storageAdapter.createFolderIfNeeded(LEGACY_DIRECTORY_KEY)
353+
}
357354

358355
for entry in selectedLegacyEntries {
359356
Self.writeForMigration(
@@ -364,7 +361,7 @@ final class UserPayloadStore {
364361
}
365362
}
366363

367-
migrationPersistenceQueue.async(flags: .barrier) {
364+
StorageService.persistenceQueue.async(flags: .barrier) {
368365
defaults.removeObject(forKey: UserDefaultsKeys.localStorageKey)
369366
defaults.removeObject(forKey: UserDefaultsKeys.cacheKeyMappingKey)
370367
StorageServiceMigrationStatus.markMigrationDone()
@@ -411,7 +408,7 @@ final class UserPayloadStore {
411408

412409
let storageAdapter = self.storageAdapter
413410
let sdkDirectoryKey = self.directoryKey
414-
persistenceQueue.async {
411+
StorageService.persistenceQueue.async(flags: .barrier) {
415412
for filename in evicted {
416413
storageAdapter.remove(sdkDirectoryKey + [filename])
417414
}

0 commit comments

Comments
 (0)