Skip to content

Commit f6dfd8e

Browse files
authored
Implement a stored event object to simplify in-memory storage (#2497)
* Implement a stored event object to simplify in-memory storage This will make passing data to the prioritizers and uploaders easier, and it significantly simplifies logic in the storage system while reducing memory footprint. * Remove two files that always seem to sneak in somehow. * Style and a needed cast
1 parent 7fa0e43 commit f6dfd8e

33 files changed

+519
-321
lines changed

GoogleDataTransport/GoogleDataTransport/Classes/GDTClock.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ - (BOOL)isAfter:(GDTClock *)otherClock {
118118
}
119119

120120
- (NSUInteger)hash {
121-
// These casts lose some precision, but it's probably fine.
122-
return (NSUInteger)_kernelBootTime ^ (NSUInteger)_uptime ^ (NSUInteger)_timeMillis;
121+
return [@(_kernelBootTime) hash] ^ [@(_uptime) hash] ^ [@(_timeMillis) hash];
123122
}
124123

125124
- (BOOL)isEqual:(id)object {

GoogleDataTransport/GoogleDataTransport/Classes/GDTEvent.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#import <GoogleDataTransport/GDTEvent.h>
1818

19+
#import <GoogleDataTransport/GDTStoredEvent.h>
20+
1921
#import "GDTAssert.h"
2022
#import "GDTEvent_Private.h"
2123

@@ -61,6 +63,10 @@ - (void)setDataObject:(id<GDTEventDataObject>)dataObject {
6163
}
6264
}
6365

66+
- (GDTStoredEvent *)storedEventWithFileURL:(NSURL *)fileURL {
67+
return [[GDTStoredEvent alloc] initWithFileURL:fileURL event:self];
68+
}
69+
6470
#pragma mark - NSSecureCoding and NSCoding Protocols
6571

6672
/** NSCoding key for mappingID property. */

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <Foundation/Foundation.h>
1818

1919
@class GDTEvent;
20+
@class GDTStoredEvent;
2021

2122
NS_ASSUME_NONNULL_BEGIN
2223

@@ -30,27 +31,17 @@ NS_ASSUME_NONNULL_BEGIN
3031
+ (instancetype)sharedInstance;
3132

3233
/** Stores event.dataObjectTransportBytes into a shared on-device folder and tracks the event via
33-
* its hash and target properties.
34-
*
35-
* @note The event param is expected to be deallocated during this method.
34+
* a GDTStoredEvent instance.
3635
*
3736
* @param event The event to store.
3837
*/
3938
- (void)storeEvent:(GDTEvent *)event;
4039

41-
/** Removes a set of event from storage specified by their hash.
42-
*
43-
* @param eventHashes The set of event hashes to remove.
44-
* @param target The upload target the event files correspond to.
45-
*/
46-
- (void)removeEvents:(NSSet<NSNumber *> *)eventHashes target:(NSNumber *)target;
47-
48-
/** Converts a set of event hashes to a set of event files.
40+
/** Removes a set of events from storage specified by their hash.
4941
*
50-
* @param eventHashes A set of event hashes to get the files of.
51-
* @return A set of equivalent length, containing all the filenames corresponding to the hashes.
42+
* @param events The set of stored events to remove.
5243
*/
53-
- (NSDictionary<NSNumber *, NSURL *> *)eventHashesToFiles:(NSSet<NSNumber *> *)eventHashes;
44+
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events;
5445

5546
@end
5647

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m

Lines changed: 57 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import "GDTStorage_Private.h"
1919

2020
#import <GoogleDataTransport/GDTPrioritizer.h>
21+
#import <GoogleDataTransport/GDTStoredEvent.h>
2122

2223
#import "GDTAssert.h"
2324
#import "GDTConsoleLogger.h"
@@ -55,8 +56,8 @@ - (instancetype)init {
5556
self = [super init];
5657
if (self) {
5758
_storageQueue = dispatch_queue_create("com.google.GDTStorage", DISPATCH_QUEUE_SERIAL);
58-
_eventHashToFile = [[NSMutableDictionary alloc] init];
59-
_targetToEventHashSet = [[NSMutableDictionary alloc] init];
59+
_targetToEventSet = [[NSMutableDictionary alloc] init];
60+
_storedEvents = [[NSMutableOrderedSet alloc] init];
6061
_uploader = [GDTUploadCoordinator sharedInstance];
6162
}
6263
return self;
@@ -65,94 +66,64 @@ - (instancetype)init {
6566
- (void)storeEvent:(GDTEvent *)event {
6667
[self createEventDirectoryIfNotExists];
6768

68-
// This is done to ensure that event is deallocated at the end of the ensuing block.
69-
__block GDTEvent *shortLivedEvent = event;
70-
__weak GDTEvent *weakShortLivedEvent = event;
71-
event = nil;
72-
7369
dispatch_async(_storageQueue, ^{
7470
// Check that a backend implementation is available for this target.
75-
NSInteger target = shortLivedEvent.target;
71+
NSInteger target = event.target;
7672

7773
// Check that a prioritizer is available for this target.
7874
id<GDTPrioritizer> prioritizer = [GDTRegistrar sharedInstance].targetToPrioritizer[@(target)];
7975
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
8076

8177
// Write the transport bytes to disk, get a filename.
82-
GDTAssert(shortLivedEvent.dataObjectTransportBytes,
83-
@"The event should have been serialized to bytes");
84-
NSURL *eventFile = [self saveEventBytesToDisk:shortLivedEvent.dataObjectTransportBytes
85-
eventHash:shortLivedEvent.hash];
78+
GDTAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes");
79+
NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes
80+
eventHash:event.hash];
81+
GDTStoredEvent *storedEvent = [event storedEventWithFileURL:eventFile];
8682

8783
// Add event to tracking collections.
88-
[self addEventToTrackingCollections:shortLivedEvent eventFile:eventFile];
84+
[self addEventToTrackingCollections:storedEvent];
8985

9086
// Check the QoS, if it's high priority, notify the target that it has a high priority event.
91-
if (shortLivedEvent.qosTier == GDTEventQoSFast) {
92-
NSSet<NSNumber *> *allEventsForTarget = self.targetToEventHashSet[@(target)];
87+
if (event.qosTier == GDTEventQoSFast) {
88+
NSSet<GDTStoredEvent *> *allEventsForTarget = self.targetToEventSet[storedEvent.target];
9389
[self.uploader forceUploadEvents:allEventsForTarget target:target];
9490
}
9591

96-
// Have the prioritizer prioritize the event, enforcing that they do not retain it.
97-
@autoreleasepool {
98-
[prioritizer prioritizeEvent:shortLivedEvent];
99-
shortLivedEvent = nil;
100-
}
101-
if (weakShortLivedEvent) {
102-
GDTLogError(GDTMCEEventWasIllegallyRetained, @"%@",
103-
@"An event should not be retained outside of storage.");
104-
};
92+
// Have the prioritizer prioritize the event.
93+
[prioritizer prioritizeEvent:storedEvent];
10594
});
10695
}
10796

108-
- (void)removeEvents:(NSSet<NSNumber *> *)eventHashes target:(NSNumber *)target {
109-
dispatch_sync(_storageQueue, ^{
110-
for (NSNumber *eventHash in eventHashes) {
111-
[self removeEvent:eventHash target:target];
112-
}
113-
});
114-
}
115-
116-
- (NSDictionary<NSNumber *, NSURL *> *)eventHashesToFiles:(NSSet<NSNumber *> *)eventHashes {
117-
NSMutableDictionary<NSNumber *, NSURL *> *eventHashesToFiles = [[NSMutableDictionary alloc] init];
118-
dispatch_sync(_storageQueue, ^{
119-
for (NSNumber *hashNumber in eventHashes) {
120-
NSURL *eventURL = self.eventHashToFile[hashNumber];
121-
GDTAssert(eventURL, @"An event file URL couldn't be found for the given hash");
122-
eventHashesToFiles[hashNumber] = eventURL;
97+
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events {
98+
NSSet<GDTStoredEvent *> *eventsToRemove = [events copy];
99+
dispatch_async(_storageQueue, ^{
100+
// Check that a prioritizer is available for this target.
101+
id<GDTPrioritizer> prioritizer;
102+
103+
for (GDTStoredEvent *event in eventsToRemove) {
104+
// Remove from disk, first and foremost.
105+
NSError *error;
106+
[[NSFileManager defaultManager] removeItemAtURL:event.eventFileURL error:&error];
107+
GDTAssert(error == nil, @"There was an error removing an event file: %@", error);
108+
109+
if (!prioritizer) {
110+
prioritizer = [GDTRegistrar sharedInstance].targetToPrioritizer[event.target];
111+
} else {
112+
GDTAssert(prioritizer == [GDTRegistrar sharedInstance].targetToPrioritizer[event.target],
113+
@"All logs within an upload set should have the same prioritizer.");
114+
}
115+
116+
// Remove from the tracking collections.
117+
[self.storedEvents removeObject:event];
118+
[self.targetToEventSet[event.target] removeObject:event];
123119
}
120+
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
121+
[prioritizer unprioritizeEvents:events];
124122
});
125-
return eventHashesToFiles;
126123
}
127124

128125
#pragma mark - Private helper methods
129126

130-
/** Removes the corresponding event file from disk.
131-
*
132-
* @param eventHash The hash value of the original event.
133-
* @param target The target of the original event.
134-
*/
135-
- (void)removeEvent:(NSNumber *)eventHash target:(NSNumber *)target {
136-
NSURL *eventFile = self.eventHashToFile[eventHash];
137-
138-
// Remove from disk, first and foremost.
139-
NSError *error;
140-
[[NSFileManager defaultManager] removeItemAtURL:eventFile error:&error];
141-
GDTAssert(error == nil, @"There was an error removing an event file: %@", error);
142-
143-
// Remove from the tracking collections.
144-
[self.eventHashToFile removeObjectForKey:eventHash];
145-
NSMutableSet<NSNumber *> *eventHashes = self.targetToEventHashSet[target];
146-
GDTAssert(eventHashes, @"There wasn't an event set for this target.");
147-
[eventHashes removeObject:eventHash];
148-
// It's fine to not remove the set if it's empty.
149-
150-
// Check that a prioritizer is available for this target.
151-
id<GDTPrioritizer> prioritizer = [GDTRegistrar sharedInstance].targetToPrioritizer[target];
152-
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
153-
[prioritizer unprioritizeEvent:eventHash];
154-
}
155-
156127
/** Creates the storage directory if it does not exist. */
157128
- (void)createEventDirectoryIfNotExists {
158129
NSError *error;
@@ -179,6 +150,9 @@ - (NSURL *)saveEventBytesToDisk:(NSData *)transportBytes eventHash:(NSUInteger)e
179150
NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash];
180151
NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]];
181152

153+
GDTAssert(![[NSFileManager defaultManager] fileExistsAtPath:eventFilePath.path],
154+
@"An event shouldn't already exist at this path: %@", eventFilePath.path);
155+
182156
BOOL writingSuccess = [transportBytes writeToURL:eventFilePath atomically:YES];
183157
if (!writingSuccess) {
184158
GDTLogError(GDTMCEFileWriteError, @"An event file could not be written: %@", eventFilePath);
@@ -193,29 +167,22 @@ - (NSURL *)saveEventBytesToDisk:(NSData *)transportBytes eventHash:(NSUInteger)e
193167
* thread safety.
194168
*
195169
* @param event The event to track.
196-
* @param eventFile The file the event has been saved to.
197170
*/
198-
- (void)addEventToTrackingCollections:(GDTEvent *)event eventFile:(NSURL *)eventFile {
199-
NSInteger target = event.target;
200-
NSNumber *eventHash = @(event.hash);
201-
NSNumber *targetNumber = @(target);
202-
self.eventHashToFile[eventHash] = eventFile;
203-
NSMutableSet<NSNumber *> *events = self.targetToEventHashSet[targetNumber];
204-
if (events) {
205-
[events addObject:eventHash];
206-
} else {
207-
NSMutableSet<NSNumber *> *eventSet = [NSMutableSet setWithObject:eventHash];
208-
self.targetToEventHashSet[targetNumber] = eventSet;
209-
}
171+
- (void)addEventToTrackingCollections:(GDTStoredEvent *)event {
172+
[_storedEvents addObject:event];
173+
NSMutableSet<GDTStoredEvent *> *events = self.targetToEventSet[event.target];
174+
events = events ? events : [[NSMutableSet alloc] init];
175+
[events addObject:event];
176+
_targetToEventSet[event.target] = events;
210177
}
211178

212179
#pragma mark - NSSecureCoding
213180

214-
/** The NSKeyedCoder key for the eventHashToFile property. */
215-
static NSString *const kGDTEventHashToFileKey = @"eventHashToFileKey";
181+
/** The NSKeyedCoder key for the storedEvents property. */
182+
static NSString *const kGDTStorageStoredEventsKey = @"GDTStorageStoredEventsKey";
216183

217-
/** The NSKeyedCoder key for the targetToEventHashSet property. */
218-
static NSString *const kGDTTargetToEventHashSetKey = @"targetToEventHashSetKey";
184+
/** The NSKeyedCoder key for the targetToEventSet property. */
185+
static NSString *const kGDTStorageTargetToEventSetKey = @"GDTStorageTargetToEventSetKey";
219186

220187
+ (BOOL)supportsSecureCoding {
221188
return YES;
@@ -225,20 +192,20 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder {
225192
// Create the singleton and populate its ivars.
226193
GDTStorage *sharedInstance = [self.class sharedInstance];
227194
dispatch_sync(sharedInstance.storageQueue, ^{
228-
Class NSMutableDictionaryClass = [NSMutableDictionary class];
229-
sharedInstance->_eventHashToFile = [aDecoder decodeObjectOfClass:NSMutableDictionaryClass
230-
forKey:kGDTEventHashToFileKey];
231-
sharedInstance->_targetToEventHashSet =
232-
[aDecoder decodeObjectOfClass:NSMutableDictionaryClass forKey:kGDTTargetToEventHashSetKey];
195+
sharedInstance->_storedEvents = [aDecoder decodeObjectOfClass:[NSMutableOrderedSet class]
196+
forKey:kGDTStorageStoredEventsKey];
197+
sharedInstance->_targetToEventSet =
198+
[aDecoder decodeObjectOfClass:[NSMutableDictionary class]
199+
forKey:kGDTStorageTargetToEventSetKey];
233200
});
234201
return sharedInstance;
235202
}
236203

237204
- (void)encodeWithCoder:(NSCoder *)aCoder {
238205
GDTStorage *sharedInstance = [self.class sharedInstance];
239206
dispatch_sync(sharedInstance.storageQueue, ^{
240-
[aCoder encodeObject:sharedInstance->_eventHashToFile forKey:kGDTEventHashToFileKey];
241-
[aCoder encodeObject:sharedInstance->_targetToEventHashSet forKey:kGDTTargetToEventHashSetKey];
207+
[aCoder encodeObject:sharedInstance->_storedEvents forKey:kGDTStorageStoredEventsKey];
208+
[aCoder encodeObject:sharedInstance->_targetToEventSet forKey:kGDTStorageTargetToEventSetKey];
242209
});
243210
}
244211

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "GDTStoredEvent.h"
18+
19+
#import <GoogleDataTransport/GDTClock.h>
20+
21+
@implementation GDTStoredEvent
22+
23+
- (instancetype)initWithFileURL:(NSURL *)URL event:(GDTEvent *)event {
24+
self = [super init];
25+
if (self) {
26+
_eventFileURL = URL;
27+
_mappingID = event.mappingID;
28+
_target = @(event.target);
29+
_qosTier = event.qosTier;
30+
_clockSnapshot = event.clockSnapshot;
31+
_customPrioritizationParams = event.customPrioritizationParams;
32+
}
33+
return self;
34+
}
35+
36+
#pragma mark - NSSecureCoding
37+
38+
/** Coding key for eventFileURL ivar. */
39+
static NSString *kEventFileURLKey = @"GDTStoredEventEventFileURLKey";
40+
41+
/** Coding key for mappingID ivar. */
42+
static NSString *kMappingIDKey = @"GDTStoredEventMappingIDKey";
43+
44+
/** Coding key for target ivar. */
45+
static NSString *kTargetKey = @"GDTStoredEventTargetKey";
46+
47+
/** Coding key for qosTier ivar. */
48+
static NSString *kQosTierKey = @"GDTStoredEventQosTierKey";
49+
50+
/** Coding key for clockSnapshot ivar. */
51+
static NSString *kClockSnapshotKey = @"GDTStoredEventClockSnapshotKey";
52+
53+
/** Coding key for customPrioritizationParams ivar. */
54+
static NSString *kCustomPrioritizationParamsKey = @"GDTStoredEventcustomPrioritizationParamsKey";
55+
56+
+ (BOOL)supportsSecureCoding {
57+
return YES;
58+
}
59+
60+
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
61+
[aCoder encodeObject:_eventFileURL forKey:kEventFileURLKey];
62+
[aCoder encodeObject:_mappingID forKey:kMappingIDKey];
63+
[aCoder encodeObject:_target forKey:kTargetKey];
64+
[aCoder encodeObject:@(_qosTier) forKey:kQosTierKey];
65+
[aCoder encodeObject:_clockSnapshot forKey:kClockSnapshotKey];
66+
[aCoder encodeObject:_customPrioritizationParams forKey:kCustomPrioritizationParamsKey];
67+
}
68+
69+
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
70+
self = [self init];
71+
if (self) {
72+
_eventFileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kEventFileURLKey];
73+
_mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kMappingIDKey];
74+
_target = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kTargetKey];
75+
NSNumber *qosTier = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kQosTierKey];
76+
_qosTier = [qosTier intValue];
77+
_clockSnapshot = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kClockSnapshotKey];
78+
_customPrioritizationParams = [aDecoder decodeObjectOfClass:[NSDictionary class]
79+
forKey:kCustomPrioritizationParamsKey];
80+
}
81+
return self;
82+
}
83+
84+
@end

GoogleDataTransport/GoogleDataTransport/Classes/GDTTransformer.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
#import "GDTAssert.h"
2323
#import "GDTConsoleLogger.h"
24-
#import "GDTEvent_Private.h"
2524
#import "GDTStorage.h"
2625

2726
@implementation GDTTransformer

GoogleDataTransport/GoogleDataTransport/Classes/GDTTransport.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
#import "GDTTransport_Private.h"
1919

2020
#import "GDTAssert.h"
21+
#import "GDTClock.h"
2122
#import "GDTEvent.h"
22-
#import "GDTEvent_Private.h"
2323
#import "GDTTransformer.h"
2424

2525
@implementation GDTTransport

0 commit comments

Comments
 (0)