|
27 | 27 | #import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h" |
28 | 28 | #import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h" |
29 | 29 |
|
| 30 | +NS_ASSUME_NONNULL_BEGIN |
| 31 | + |
30 | 32 | /** A library data key this class uses to track batchIDs. */ |
31 | 33 | static NSString *const gBatchIDCounterKey = @"GDTCORFlatFileStorageBatchIDCounter"; |
32 | 34 |
|
@@ -225,52 +227,7 @@ - (void)removeBatchWithID:(nonnull NSNumber *)batchID |
225 | 227 | deleteEvents:(BOOL)deleteEvents |
226 | 228 | onComplete:(void (^_Nullable)(void))onComplete { |
227 | 229 | dispatch_async(_storageQueue, ^{ |
228 | | - NSError *error; |
229 | | - NSArray<NSString *> *batchDirPaths = [self batchDirPathsForBatchID:batchID error:&error]; |
230 | | - |
231 | | - if (batchDirPaths == nil) { |
232 | | - if (onComplete) { |
233 | | - onComplete(); |
234 | | - } |
235 | | - return; |
236 | | - } |
237 | | - |
238 | | - NSFileManager *fileManager = [NSFileManager defaultManager]; |
239 | | - |
240 | | - void (^removeBatchDir)(NSString *batchDirPath) = ^(NSString *batchDirPath) { |
241 | | - NSError *error; |
242 | | - if ([fileManager removeItemAtPath:batchDirPath error:&error]) { |
243 | | - GDTCORLogDebug(@"Batch removed at path: %@", batchDirPath); |
244 | | - } else { |
245 | | - GDTCORLogDebug(@"Failed to remove batch at path: %@", batchDirPath); |
246 | | - } |
247 | | - }; |
248 | | - |
249 | | - for (NSString *batchDirPath in batchDirPaths) { |
250 | | - if (deleteEvents) { |
251 | | - removeBatchDir(batchDirPath); |
252 | | - } else { |
253 | | - NSString *batchDirName = [batchDirPath lastPathComponent]; |
254 | | - NSDictionary<NSString *, id> *components = [self batchComponentsFromFilename:batchDirName]; |
255 | | - NSNumber *target = components[kGDTCORBatchComponentsTargetKey]; |
256 | | - NSString *destinationPath = [[GDTCORFlatFileStorage eventDataStoragePath] |
257 | | - stringByAppendingPathComponent:target.stringValue]; |
258 | | - |
259 | | - // `- [NSFileManager moveItemAtPath:toPath:error:] method fails if an item by the |
260 | | - // destination path already exists (which usually is the case for the current method). Move |
261 | | - // the events one by one instead. |
262 | | - if ([self moveContentsOfDirectoryAtPath:batchDirPath to:destinationPath error:&error]) { |
263 | | - GDTCORLogDebug(@"Batched events at path: %@ moved back to the storage: %@", batchDirPath, |
264 | | - destinationPath); |
265 | | - } else { |
266 | | - GDTCORLogDebug(@"Error encountered whilst moving events back: %@", error); |
267 | | - } |
268 | | - |
269 | | - // Even if not all events where moved back to the storage, there is not much can be done at |
270 | | - // this point, so cleanup batch directory now to avoid clattering. |
271 | | - removeBatchDir(batchDirPath); |
272 | | - } |
273 | | - } |
| 230 | + [self syncThreadUnsafeRemoveBatchWithID:batchID deleteEvents:deleteEvents]; |
274 | 231 |
|
275 | 232 | if (onComplete) { |
276 | 233 | onComplete(); |
@@ -384,45 +341,48 @@ - (void)hasEventsForTarget:(GDTCORTarget)target onComplete:(void (^)(BOOL hasEve |
384 | 341 |
|
385 | 342 | - (void)checkForExpirations { |
386 | 343 | dispatch_async(_storageQueue, ^{ |
387 | | - NSMutableSet<NSString *> *pathsToDelete = [[NSMutableSet alloc] init]; |
388 | 344 | GDTCORLogDebug(@"%@", @"Checking for expired events and batches"); |
389 | 345 | NSTimeInterval now = [NSDate date].timeIntervalSince1970; |
390 | 346 | NSFileManager *fileManager = [NSFileManager defaultManager]; |
391 | | - NSString *eventDataPath = [GDTCORFlatFileStorage eventDataStoragePath]; |
392 | | - NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:eventDataPath]; |
393 | | - NSString *path; |
394 | | - while ((path = [enumerator nextObject])) { |
395 | | - NSString *fileName = [path lastPathComponent]; |
396 | | - NSDictionary<NSString *, id> *eventComponents = [self eventComponentsFromFilename:fileName]; |
397 | | - NSDate *expirationDate = eventComponents[kGDTCOREventComponentsExpirationKey]; |
398 | | - if (expirationDate != nil && expirationDate.timeIntervalSince1970 < now) { |
399 | | - [pathsToDelete addObject:[eventDataPath stringByAppendingPathComponent:path]]; |
400 | | - } |
401 | | - } |
402 | 347 |
|
403 | | - // TODO: Events from expired batches with not expired events must be moved back to queue to |
404 | | - // avoid data loss. |
405 | 348 | // TODO: Storage may not have enough context to remove batches because a batch may be being |
406 | 349 | // uploaded but the storage has not context of it. |
| 350 | + |
| 351 | + // Find expired batches and move their events back to the main storage. |
| 352 | + // If a batch contains expired events they are expected to be removed further in the method |
| 353 | + // together with other expired events in the main storage. |
407 | 354 | NSString *batchDataPath = [GDTCORFlatFileStorage batchDataStoragePath]; |
408 | 355 | NSArray<NSString *> *batchDataPaths = [fileManager contentsOfDirectoryAtPath:batchDataPath |
409 | 356 | error:nil]; |
410 | 357 | for (NSString *path in batchDataPaths) { |
411 | 358 | NSString *fileName = [path lastPathComponent]; |
412 | 359 | NSDictionary<NSString *, id> *batchComponents = [self batchComponentsFromFilename:fileName]; |
413 | 360 | NSDate *expirationDate = batchComponents[kGDTCORBatchComponentsExpirationKey]; |
414 | | - if (expirationDate != nil && expirationDate.timeIntervalSince1970 < now) { |
415 | | - [pathsToDelete addObject:[batchDataPath stringByAppendingPathComponent:path]]; |
| 361 | + NSNumber *batchID = batchComponents[kGDTCORBatchComponentsBatchIDKey]; |
| 362 | + if (expirationDate != nil && expirationDate.timeIntervalSince1970 < now && batchID != nil) { |
| 363 | + NSNumber *batchID = batchComponents[kGDTCORBatchComponentsBatchIDKey]; |
| 364 | + // Move all events from the expired batch back to the main storage. |
| 365 | + [self syncThreadUnsafeRemoveBatchWithID:batchID deleteEvents:NO]; |
416 | 366 | } |
417 | 367 | } |
418 | 368 |
|
419 | | - for (NSString *path in pathsToDelete) { |
420 | | - NSError *error; |
421 | | - [fileManager removeItemAtPath:path error:&error]; |
422 | | - if (error != nil) { |
423 | | - GDTCORLogDebug(@"There was an error deleting an expired item: %@", error); |
424 | | - } else { |
425 | | - GDTCORLogDebug(@"Item deleted because it expired: %@", path); |
| 369 | + // Find expired events and remove them from the storage. |
| 370 | + NSString *eventDataPath = [GDTCORFlatFileStorage eventDataStoragePath]; |
| 371 | + NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:eventDataPath]; |
| 372 | + NSString *path; |
| 373 | + while ((path = [enumerator nextObject])) { |
| 374 | + NSString *fileName = [path lastPathComponent]; |
| 375 | + NSDictionary<NSString *, id> *eventComponents = [self eventComponentsFromFilename:fileName]; |
| 376 | + NSDate *expirationDate = eventComponents[kGDTCOREventComponentsExpirationKey]; |
| 377 | + if (expirationDate != nil && expirationDate.timeIntervalSince1970 < now) { |
| 378 | + NSString *pathToDelete = [eventDataPath stringByAppendingPathComponent:path]; |
| 379 | + NSError *error; |
| 380 | + [fileManager removeItemAtPath:pathToDelete error:&error]; |
| 381 | + if (error != nil) { |
| 382 | + GDTCORLogDebug(@"There was an error deleting an expired item: %@", error); |
| 383 | + } else { |
| 384 | + GDTCORLogDebug(@"Item deleted because it expired: %@", pathToDelete); |
| 385 | + } |
426 | 386 | } |
427 | 387 | } |
428 | 388 | }); |
@@ -539,6 +499,53 @@ - (BOOL)moveContentsOfDirectoryAtPath:(NSString *)sourcePath |
539 | 499 | } |
540 | 500 | } |
541 | 501 |
|
| 502 | +- (void)syncThreadUnsafeRemoveBatchWithID:(nonnull NSNumber *)batchID |
| 503 | + deleteEvents:(BOOL)deleteEvents { |
| 504 | + NSError *error; |
| 505 | + NSArray<NSString *> *batchDirPaths = [self batchDirPathsForBatchID:batchID error:&error]; |
| 506 | + |
| 507 | + if (batchDirPaths == nil) { |
| 508 | + return; |
| 509 | + } |
| 510 | + |
| 511 | + NSFileManager *fileManager = [NSFileManager defaultManager]; |
| 512 | + |
| 513 | + void (^removeBatchDir)(NSString *batchDirPath) = ^(NSString *batchDirPath) { |
| 514 | + NSError *error; |
| 515 | + if ([fileManager removeItemAtPath:batchDirPath error:&error]) { |
| 516 | + GDTCORLogDebug(@"Batch removed at path: %@", batchDirPath); |
| 517 | + } else { |
| 518 | + GDTCORLogDebug(@"Failed to remove batch at path: %@", batchDirPath); |
| 519 | + } |
| 520 | + }; |
| 521 | + |
| 522 | + for (NSString *batchDirPath in batchDirPaths) { |
| 523 | + if (deleteEvents) { |
| 524 | + removeBatchDir(batchDirPath); |
| 525 | + } else { |
| 526 | + NSString *batchDirName = [batchDirPath lastPathComponent]; |
| 527 | + NSDictionary<NSString *, id> *components = [self batchComponentsFromFilename:batchDirName]; |
| 528 | + NSNumber *target = components[kGDTCORBatchComponentsTargetKey]; |
| 529 | + NSString *destinationPath = [[GDTCORFlatFileStorage eventDataStoragePath] |
| 530 | + stringByAppendingPathComponent:target.stringValue]; |
| 531 | + |
| 532 | + // `- [NSFileManager moveItemAtPath:toPath:error:]` method fails if an item by the |
| 533 | + // destination path already exists (which usually is the case for the current method). Move |
| 534 | + // the events one by one instead. |
| 535 | + if ([self moveContentsOfDirectoryAtPath:batchDirPath to:destinationPath error:&error]) { |
| 536 | + GDTCORLogDebug(@"Batched events at path: %@ moved back to the storage: %@", batchDirPath, |
| 537 | + destinationPath); |
| 538 | + } else { |
| 539 | + GDTCORLogDebug(@"Error encountered whilst moving events back: %@", error); |
| 540 | + } |
| 541 | + |
| 542 | + // Even if not all events where moved back to the storage, there is not much can be done at |
| 543 | + // this point, so cleanup batch directory now to avoid clattering. |
| 544 | + removeBatchDir(batchDirPath); |
| 545 | + } |
| 546 | + } |
| 547 | +} |
| 548 | + |
542 | 549 | #pragma mark - Private helper methods |
543 | 550 |
|
544 | 551 | + (NSString *)eventDataStoragePath { |
@@ -766,3 +773,5 @@ - (void)appWillTerminate:(GDTCORApplication *)application { |
766 | 773 | } |
767 | 774 |
|
768 | 775 | @end |
| 776 | + |
| 777 | +NS_ASSUME_NONNULL_END |
0 commit comments