|
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