Skip to content

Commit a61786c

Browse files
authored
Refactor event saving and searching (#5773)
* Refactor event saving and searching No more symlinks to try and speed searching. Just get a directory's contents and check the filename, which will contain metadata about the event. * Change one string formatter * Fix the type of some params, remove unused code * Minor update * Continue running after failing to process a file
1 parent f33cde6 commit a61786c

File tree

5 files changed

+114
-164
lines changed

5 files changed

+114
-164
lines changed

GoogleDataTransport/GDTCORLibrary/GDTCORFlatFileStorage.m

Lines changed: 67 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -102,74 +102,71 @@ + (NSString *)libraryDataStoragePath {
102102
return libraryDataPath;
103103
}
104104

105-
+ (NSDictionary<NSString *, NSString *> *)pathsForEvent:(GDTCOREvent *)event {
106-
NSString *dataPath =
107-
[NSString stringWithFormat:@"%@/%ld/%@", [GDTCORFlatFileStorage baseEventStoragePath],
108-
(long)event.target, event.eventID];
109-
NSString *mappingIDPath =
110-
[NSString stringWithFormat:@"%@/%ld/%@/%@", [GDTCORFlatFileStorage baseEventStoragePath],
111-
(long)event.target, event.mappingID, event.eventID];
112-
NSString *qosTierPath =
113-
[NSString stringWithFormat:@"%@/%ld/%ld/%@", [GDTCORFlatFileStorage baseEventStoragePath],
114-
(long)event.target, (long)event.qosTier, event.eventID];
115-
return @{
116-
gGDTCORFlatFileStorageEventDataPathKey : dataPath,
117-
gGDTCORFlatFileStorageMappingIDPathKey : mappingIDPath,
118-
gGDTCORFlatFileStorageQoSTierPathKey : qosTierPath
119-
};
120-
}
121-
122105
+ (NSString *)pathForTarget:(GDTCORTarget)target
123-
qosTier:(nullable NSNumber *)qosTier
124-
mappingID:(nullable NSString *)mappingID {
125-
NSString *baseEventPath = [GDTCORFlatFileStorage baseEventStoragePath];
126-
// If only a target was given, return the target path.
127-
if (qosTier == nil && mappingID == nil) {
128-
return [NSString stringWithFormat:@"%@/%ld", baseEventPath, (long)target];
129-
}
130-
131-
// If only a target and mappingID were given, return the mapping ID path.
132-
if (qosTier == nil) {
133-
return [NSString stringWithFormat:@"%@/%ld/%@", baseEventPath, (long)target, mappingID];
134-
}
135-
136-
// If only a target and qosTier were given, return the QoS tier path.
137-
if (mappingID == nil) {
138-
return [NSString stringWithFormat:@"%@/%ld/%@", baseEventPath, (long)target, qosTier];
139-
}
140-
141-
// If a target, mappingID, and qosTier were all given, return a single target/qosTier/mappingID
142-
// directory.
106+
eventID:(NSNumber *)eventID
107+
qosTier:(NSNumber *)qosTier
108+
mappingID:(NSString *)mappingID {
143109
return
144-
[NSString stringWithFormat:@"%@/%ld/%@/%@", baseEventPath, (long)target, qosTier, mappingID];
110+
[NSString stringWithFormat:@"%@/%ld/%@.%@.%@", [GDTCORFlatFileStorage baseEventStoragePath],
111+
(long)target, eventID, qosTier, mappingID];
145112
}
146113

147-
+ (NSArray<NSString *> *)searchPathsWithEventSelector:(GDTCORStorageEventSelector *)eventSelector {
148-
NSMutableArray<NSString *> *searchPaths = [[NSMutableArray alloc] init];
149-
if (eventSelector.selectedQosTiers && eventSelector.selectedQosTiers.count > 0) {
150-
for (NSNumber *qosTier in eventSelector.selectedQosTiers) {
151-
NSString *searchPath = [self pathForTarget:eventSelector.selectedTarget
152-
qosTier:qosTier
153-
mappingID:eventSelector.selectedMappingID];
154-
BOOL isDirectory;
155-
if ([[NSFileManager defaultManager] fileExistsAtPath:searchPath isDirectory:&isDirectory]) {
156-
if (isDirectory) {
157-
[searchPaths addObject:searchPath];
158-
}
159-
}
114+
+ (NSSet<NSString *> *)pathsForTarget:(GDTCORTarget)target
115+
eventIDs:(nullable NSSet<NSNumber *> *)eventIDs
116+
qosTiers:(nullable NSSet<NSNumber *> *)qosTiers
117+
mappingIDs:(nullable NSSet<NSString *> *)mappingIDs {
118+
NSMutableSet<NSString *> *paths = [[NSMutableSet alloc] init];
119+
NSFileManager *fileManager = [NSFileManager defaultManager];
120+
NSString *targetPath = [NSString
121+
stringWithFormat:@"%@/%ld", [GDTCORFlatFileStorage baseEventStoragePath], (long)target];
122+
NSError *error;
123+
NSArray<NSString *> *dirPaths = [fileManager contentsOfDirectoryAtPath:targetPath error:&error];
124+
if (error) {
125+
GDTCORLogDebug(@"There was an error reading the contents of the target path: %@", error);
126+
return paths;
127+
}
128+
BOOL checkingIDs = eventIDs.count > 0;
129+
BOOL checkingQosTiers = qosTiers.count > 0;
130+
BOOL checkingMappingIDs = mappingIDs.count > 0;
131+
BOOL checkingAnything = checkingIDs == NO && checkingQosTiers == NO && checkingMappingIDs == NO;
132+
for (NSString *path in dirPaths) {
133+
if (checkingAnything) {
134+
[paths addObject:path];
135+
continue;
160136
}
161-
} else {
162-
NSString *searchPath = [self pathForTarget:eventSelector.selectedTarget
163-
qosTier:nil
164-
mappingID:eventSelector.selectedMappingID];
165-
BOOL isDirectory;
166-
if ([[NSFileManager defaultManager] fileExistsAtPath:searchPath isDirectory:&isDirectory]) {
167-
if (isDirectory) {
168-
[searchPaths addObject:searchPath];
169-
}
137+
NSString *filename = [path lastPathComponent];
138+
NSArray<NSString *> *components = [filename componentsSeparatedByString:@"."];
139+
if (components.count != 3) {
140+
GDTCORLogDebug(@"There was an error reading the filename components: %@", components);
141+
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
142+
continue;
143+
}
144+
NSNumber *eventID = @(components[0].integerValue);
145+
NSNumber *qosTier = @(components[1].integerValue);
146+
NSString *mappingID = components[2];
147+
if (eventID == nil || qosTier == nil) {
148+
GDTCORLogDebug(@"There was an error parsing the filename components: %@", components);
149+
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
150+
continue;
151+
}
152+
NSNumber *eventIDMatch = checkingIDs ? @([eventIDs containsObject:eventID]) : nil;
153+
NSNumber *qosTierMatch = checkingQosTiers ? @([qosTiers containsObject:qosTier]) : nil;
154+
NSNumber *mappingIDMatch = checkingMappingIDs ? @([mappingIDs containsObject:mappingID]) : nil;
155+
if ((eventIDMatch == nil || eventIDMatch.boolValue) &&
156+
(qosTierMatch == nil || qosTierMatch.boolValue) &&
157+
(mappingIDMatch == nil || mappingIDMatch.boolValue)) {
158+
[paths addObject:path];
170159
}
171160
}
172-
return searchPaths;
161+
return paths;
162+
}
163+
164+
+ (NSArray<NSString *> *)searchPathsWithEventSelector:(GDTCORStorageEventSelector *)eventSelector {
165+
NSSet *searchPaths = [GDTCORFlatFileStorage pathsForTarget:eventSelector.selectedTarget
166+
eventIDs:eventSelector.selectedEventIDs
167+
qosTiers:eventSelector.selectedQosTiers
168+
mappingIDs:eventSelector.selectedMappingIDs];
169+
return [searchPaths allObjects];
173170
}
174171

175172
+ (instancetype)sharedInstance {
@@ -357,38 +354,23 @@ - (void)removeLibraryDataForKey:(nonnull NSString *)key
357354
}
358355

359356
- (BOOL)hasEventsForTarget:(GDTCORTarget)target {
360-
NSString *searchPath = [GDTCORFlatFileStorage pathForTarget:target qosTier:nil mappingID:nil];
361-
NSFileManager *fm = [NSFileManager defaultManager];
362-
NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath:searchPath];
363-
while ([enumerator nextObject]) {
364-
if ([enumerator.fileAttributes[NSFileType] isEqual:NSFileTypeRegular]) {
365-
return YES;
366-
}
367-
}
368-
return NO;
357+
// TODO(mikehaney24): Increase the performance of this call.
358+
GDTCORStorageEventSelector *eventSelector =
359+
[[GDTCORStorageEventSelector alloc] initWithTarget:target
360+
eventIDs:nil
361+
mappingIDs:nil
362+
qosTiers:nil];
363+
id<GDTCORStorageEventIterator> iter = [self iteratorWithSelector:eventSelector];
364+
return [iter nextEvent] != nil;
369365
}
370366

371367
- (nullable id<GDTCORStorageEventIterator>)iteratorWithSelector:
372368
(nonnull GDTCORStorageEventSelector *)eventSelector {
373369
__block GDTCORFlatFileStorageIterator *iterator;
374370
dispatch_sync(_storageQueue, ^{
375-
NSMutableArray<NSString *> *filePaths;
376371
NSArray<NSString *> *searchPaths =
377372
[GDTCORFlatFileStorage searchPathsWithEventSelector:eventSelector];
378-
for (NSString *searchPath in searchPaths) {
379-
NSDirectoryEnumerator *enumerator =
380-
[[NSFileManager defaultManager] enumeratorAtPath:searchPath];
381-
NSString *nextFile;
382-
while ((nextFile = [enumerator nextObject])) {
383-
NSFileAttributeType fileType = enumerator.fileAttributes[NSFileType];
384-
if ([fileType isEqual:NSFileTypeRegular]) {
385-
[filePaths addObject:nextFile];
386-
}
387-
}
388-
iterator = [[GDTCORFlatFileStorageIterator alloc] initWithTarget:eventSelector.selectedTarget
389-
queue:self->_storageQueue];
390-
iterator.eventFiles = filePaths;
391-
}
373+
iterator.eventFiles = searchPaths;
392374
});
393375
return iterator;
394376
}

GoogleDataTransport/GDTCORLibrary/GDTCORStorageEventSelector.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
@implementation GDTCORStorageEventSelector
2020

2121
- (instancetype)initWithTarget:(GDTCORTarget)target
22-
eventIDEqualTo:(nullable NSNumber *)eventID
23-
mappingIDEqualTo:(nullable NSString *)mappingID
24-
qosTiers:(nullable NSArray<NSNumber *> *)qosTiers {
22+
eventIDs:(nullable NSSet<NSNumber *> *)eventIDs
23+
mappingIDs:(nullable NSSet<NSString *> *)mappingIDs
24+
qosTiers:(nullable NSSet<NSNumber *> *)qosTiers {
2525
self = [super init];
2626
if (self) {
2727
_selectedTarget = target;
28-
_selectedEventID = eventID;
29-
_selectedMappingID = mappingID;
28+
_selectedEventIDs = eventIDs;
29+
_selectedMappingIDs = mappingIDs;
3030
_selectedQosTiers = qosTiers;
3131
}
3232
return self;

GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,10 @@
2525

2626
NS_ASSUME_NONNULL_BEGIN
2727

28-
/** The key of the event data path if a path dictionary is returned. */
29-
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageEventDataPathKey;
30-
31-
/** The key of the event's mapping ID path if a path dictionary is returned. */
32-
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageMappingIDPathKey;
33-
34-
/** The key of the event's qos tier path if a path dictionary is returned. */
35-
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageQoSTierPathKey;
36-
3728
/** Manages the storage of events. This class is thread-safe.
3829
*
3930
* Event files will be stored as follows:
40-
* <app cache>/gdt_event_data/<target>/<eventID> as a normal file write
41-
* <app cache>/gdt_event_data/<target>/<qosTier>/<eventID> as a symbolic link
42-
* <app cache>/gdt_event_data/<target>/<mappingID>/<eventID> as a symbolic link
31+
* <app cache>/gdt_event_data/<target>/<eventID>.<qosTier>.<mappingID>
4332
*
4433
* Library data will be stored as follows:
4534
* <app cache>/gdt_library_data/<key of library data>
@@ -85,24 +74,29 @@ FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageQoSTierPathKey;
8574
*/
8675
+ (NSString *)libraryDataStoragePath;
8776

88-
/** Returns storage paths for the given event, though the paths may not exist.
89-
*
90-
* @note The keys of this dictionary are declared in this header.
91-
* @param event The event to map to storage paths.
92-
*/
93-
+ (NSDictionary<NSString *, NSString *> *)pathsForEvent:(GDTCOREvent *)event;
94-
95-
/** Returns a storage path to events for the given target, qosTier, and mapping ID. The path may not
96-
* exist.
77+
/** Returns a constructed storage path based on the given values. This path may not exist.
9778
*
9879
* @param target The target, which is necessary to be given a path.
99-
* @param qosTier An optional parameter to get a more specific path.
100-
* @param mappingID An optional parameter to get a more specific path.
80+
* @param eventID The eventID.
81+
* @param qosTier The qosTier.
82+
* @param mappingID The mappingID.
10183
* @return The path representing the combination of the given parameters.
10284
*/
10385
+ (NSString *)pathForTarget:(GDTCORTarget)target
104-
qosTier:(nullable NSNumber *)qosTier
105-
mappingID:(nullable NSString *)mappingID;
86+
eventID:(NSNumber *)eventID
87+
qosTier:(NSNumber *)qosTier
88+
mappingID:(NSString *)mappingID;
89+
90+
/** Returns extant paths that match all of the given parameters.
91+
*
92+
* @param eventIDs The list of eventIDs to look for, or nil for any.
93+
* @param qosTiers The list of qosTiers to look for, or nil for any.
94+
* @param mappingIDs The list of mappingIDs to look for, or nil for any.
95+
*/
96+
+ (NSSet<NSString *> *)pathsForTarget:(GDTCORTarget)target
97+
eventIDs:(nullable NSSet<NSNumber *> *)eventIDs
98+
qosTiers:(nullable NSSet<NSNumber *> *)qosTiers
99+
mappingIDs:(nullable NSSet<NSString *> *)mappingIDs;
106100

107101
/** Returns a list of paths that will contain events for the given event selector.
108102
*

GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageEventSelector.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,26 @@ NS_ASSUME_NONNULL_BEGIN
2828
@property(readonly, nonatomic) GDTCORTarget selectedTarget;
2929

3030
/** Finds a specific event. */
31-
@property(nullable, readonly, nonatomic) NSNumber *selectedEventID;
31+
@property(nullable, readonly, nonatomic) NSSet<NSNumber *> *selectedEventIDs;
3232

3333
/** Finds all events of a mappingID. */
34-
@property(nullable, readonly, nonatomic) NSString *selectedMappingID;
34+
@property(nullable, readonly, nonatomic) NSSet<NSString *> *selectedMappingIDs;
3535

3636
/** Finds all events matching the qosTiers in this list. */
37-
@property(nullable, readonly, nonatomic) NSArray<NSNumber *> *selectedQosTiers;
37+
@property(nullable, readonly, nonatomic) NSSet<NSNumber *> *selectedQosTiers;
3838

3939
/** Instantiates an event selector.
4040
*
4141
* @param target The selected target.
42-
* @param eventID Optional param to find an event matching this eventID.
43-
* @param mappingID Optional param to find events matching this mappingID.
42+
* @param eventIDs Optional param to find an event matching this eventID.
43+
* @param mappingIDs Optional param to find events matching this mappingID.
4444
* @param qosTiers Optional param to find events matching the given QoS tiers.
4545
* @return An immutable event selector instance.
4646
*/
4747
- (instancetype)initWithTarget:(GDTCORTarget)target
48-
eventIDEqualTo:(nullable NSNumber *)eventID
49-
mappingIDEqualTo:(nullable NSString *)mappingID
50-
qosTiers:(nullable NSArray<NSNumber *> *)qosTiers;
48+
eventIDs:(nullable NSSet<NSNumber *> *)eventIDs
49+
mappingIDs:(nullable NSSet<NSString *> *)mappingIDs
50+
qosTiers:(nullable NSSet<NSNumber *> *)qosTiers;
5151
@end
5252

5353
NS_ASSUME_NONNULL_END

GoogleDataTransport/GDTCORTests/Unit/GDTCORFlatFileStorageTest.m

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -525,49 +525,23 @@ - (void)testSaveAndRemoveLibraryData {
525525
[self waitForExpectations:@[ expectation ] timeout:10.0];
526526
}
527527

528-
/** Tests that -pathsForEvent returns 3 file paths of the appropriate structure. */
529-
- (void)testPathsForEvent {
530-
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"test" target:kGDTCORTargetTest];
531-
NSDictionary<NSString *, NSString *> *eventPaths = [GDTCORFlatFileStorage pathsForEvent:event];
528+
/** Tests that -pathForTarget:qosTier:mappingID: returns the correctly structured path. */
529+
- (void)testPathsForTargetEventIDQoSTierMappingID {
530+
// TODO(mikehaney24): Complete below tests once events are being saved to disk.
532531

533-
NSString *dataPathSuffix = event.eventID.stringValue;
534-
XCTAssertTrue([eventPaths[gGDTCORFlatFileStorageEventDataPathKey] hasSuffix:dataPathSuffix]);
532+
// Store some predictably generated events across > 1 target.
535533

536-
NSString *qosTierSuffix =
537-
[NSString stringWithFormat:@"%ld/%@/%@", (long)event.target, @(event.qosTier), event.eventID];
538-
XCTAssertTrue([eventPaths[gGDTCORFlatFileStorageQoSTierPathKey] hasSuffix:qosTierSuffix]);
534+
// Search for all events (by target).
539535

540-
NSString *mappingIDSuffix =
541-
[NSString stringWithFormat:@"%ld/%@/%@", (long)event.target, event.mappingID, event.eventID];
542-
XCTAssertTrue([eventPaths[gGDTCORFlatFileStorageMappingIDPathKey] hasSuffix:mappingIDSuffix]);
543-
}
536+
// Search for events by eventID.
544537

545-
/** Tests that -pathForTarget:qosTier:mappingID: returns the correctly structured path. */
546-
- (void)testPathForTargetQoSTier {
547-
// Target only.
548-
NSString *expectedSuffix = [NSString stringWithFormat:@"%ld", (long)kGDTCORTargetTest];
549-
NSString *path = [GDTCORFlatFileStorage pathForTarget:kGDTCORTargetTest
550-
qosTier:nil
551-
mappingID:nil];
552-
XCTAssertTrue([path hasSuffix:expectedSuffix]);
553-
554-
// qosTier is given.
555-
path = [GDTCORFlatFileStorage pathForTarget:kGDTCORTargetTest
556-
qosTier:@(GDTCOREventQoSFast)
557-
mappingID:nil];
558-
XCTAssertTrue([path hasSuffix:@(GDTCOREventQoSFast).stringValue]);
559-
560-
// mappingID is given.
561-
path = [GDTCORFlatFileStorage pathForTarget:kGDTCORTargetTest qosTier:nil mappingID:@"test"];
562-
XCTAssertTrue([path hasSuffix:@"test"]);
563-
564-
// Both qosTier and mappingID are given.
565-
path = [GDTCORFlatFileStorage pathForTarget:kGDTCORTargetTest
566-
qosTier:@(GDTCOREventQoSFast)
567-
mappingID:@"test"];
568-
expectedSuffix = [NSString
569-
stringWithFormat:@"%ld/%ld/%@", (long)kGDTCORTargetTest, (long)GDTCOREventQoSFast, @"test"];
570-
XCTAssertTrue([path hasSuffix:expectedSuffix]);
538+
// Search for events by qosTier.
539+
540+
// Search for events by mappingID.
541+
542+
// Search for events by eventID and qosTier.
543+
544+
// Search for events by qosTier and mappingID.
571545
}
572546

573547
/** Tests that searchPathsWithEventSelector: returns the appropriate event search paths. */
@@ -586,8 +560,8 @@ - (void)testHasEventsForTarget {
586560
- (void)testIteratorWithSelector {
587561
GDTCORStorageEventSelector *eventSelector =
588562
[[GDTCORStorageEventSelector alloc] initWithTarget:kGDTCORTargetTest
589-
eventIDEqualTo:nil
590-
mappingIDEqualTo:nil
563+
eventIDs:nil
564+
mappingIDs:nil
591565
qosTiers:nil];
592566
id<GDTCORStorageEventIterator> iter =
593567
[[GDTCORFlatFileStorage sharedInstance] iteratorWithSelector:eventSelector];

0 commit comments

Comments
 (0)