Skip to content

Commit db3da2e

Browse files
authored
Make high priority events force an upload via upload conditions (#2699)
* Make high priority events force an upload via upload conditions Forced uploads should be defined as an additional upload condition, as opposed to constructing an ad-hoc upload package. This allows the prioritizer and uploader to do housekeeping more easily. Address a race condition during high-priority event sending in which a package can be asked for whilst the storage is still asking the prioritizer to deprioritize events that have already been removed from disk. * Make _timeMillis in GDTClock be the actual unix time * Style
1 parent 959e8b9 commit db3da2e

File tree

8 files changed

+45
-44
lines changed

8 files changed

+45
-44
lines changed

GoogleDataTransport/GoogleDataTransport/Classes/GDTClock.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ - (instancetype)init {
8181
if (self) {
8282
_kernelBootTime = KernelBootTimeInNanoseconds();
8383
_uptime = UptimeInNanoseconds();
84-
_timeMillis = CFAbsoluteTimeGetCurrent() * NSEC_PER_USEC;
84+
_timeMillis =
85+
(int64_t)((CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) * NSEC_PER_USEC);
8586
CFTimeZoneRef timeZoneRef = CFTimeZoneCopySystem();
8687
_timezoneOffsetSeconds = CFTimeZoneGetSecondsFromGMT(timeZoneRef, 0);
8788
CFRelease(timeZoneRef);

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,42 +83,37 @@ - (void)storeEvent:(GDTEvent *)event {
8383
// Add event to tracking collections.
8484
[self addEventToTrackingCollections:storedEvent];
8585

86+
// Have the prioritizer prioritize the event.
87+
[prioritizer prioritizeEvent:storedEvent];
88+
8689
// Check the QoS, if it's high priority, notify the target that it has a high priority event.
8790
if (event.qosTier == GDTEventQoSFast) {
88-
NSSet<GDTStoredEvent *> *allEventsForTarget = self.targetToEventSet[storedEvent.target];
89-
[self.uploader forceUploadEvents:allEventsForTarget target:target];
91+
[self.uploader forceUploadForTarget:target];
9092
}
91-
92-
// Have the prioritizer prioritize the event.
93-
[prioritizer prioritizeEvent:storedEvent];
9493
});
9594
}
9695

9796
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events {
9897
NSSet<GDTStoredEvent *> *eventsToRemove = [events copy];
99-
dispatch_async(_storageQueue, ^{
100-
// Check that a prioritizer is available for this target.
101-
id<GDTPrioritizer> prioritizer;
98+
GDTStoredEvent *anyEvent = [eventsToRemove anyObject];
99+
id<GDTPrioritizer> prioritizer =
100+
[GDTRegistrar sharedInstance].targetToPrioritizer[anyEvent.target];
101+
GDTAssert(prioritizer, @"There must be a prioritizer.");
102+
[prioritizer unprioritizeEvents:events];
102103

104+
dispatch_async(_storageQueue, ^{
103105
for (GDTStoredEvent *event in eventsToRemove) {
104106
// Remove from disk, first and foremost.
105107
NSError *error;
106108
[[NSFileManager defaultManager] removeItemAtURL:event.eventFileURL error:&error];
107109
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-
}
110+
GDTAssert([GDTRegistrar sharedInstance].targetToPrioritizer[event.target] == prioritizer,
111+
@"All logs within an upload set should have the same prioritizer.");
115112

116113
// Remove from the tracking collections.
117114
[self.storedEvents removeObject:event];
118115
[self.targetToEventSet[event.target] removeObject:event];
119116
}
120-
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
121-
[prioritizer unprioritizeEvents:events];
122117
});
123118
}
124119

GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ NS_ASSUME_NONNULL_BEGIN
3434
/** Forces the backend specified by the target to upload the provided set of events. This should
3535
* only ever happen when the QoS tier of an event requires it.
3636
*
37-
* @param events The set of event hashes to force upload.
3837
* @param target The target that should force an upload.
3938
*/
40-
- (void)forceUploadEvents:(NSSet<GDTStoredEvent *> *)events target:(GDTTarget)target;
39+
- (void)forceUploadForTarget:(GDTTarget)target;
4140

4241
@end
4342

GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,23 @@ - (instancetype)init {
5151
return self;
5252
}
5353

54-
- (void)forceUploadEvents:(NSSet<GDTStoredEvent *> *)events target:(GDTTarget)target {
54+
- (void)forceUploadForTarget:(GDTTarget)target {
5555
dispatch_async(_coordinationQueue, ^{
5656
GDTLogWarning(GDTMCWForcedUpload, @"%@", @"A high priority event has caused an upload.");
5757
NSNumber *targetNumber = @(target);
58-
GDTRegistrar *registrar = self->_registrar;
5958
GDTUploadCoordinatorForceUploadBlock forceUploadBlock = ^{
60-
GDTAssert(events.count, @"It doesn't make sense to force upload of 0 events");
61-
id<GDTUploader> uploader = registrar.targetToUploader[targetNumber];
62-
GDTUploadPackage *package = [[GDTUploadPackage alloc] init];
63-
package.events = [events copy];
64-
GDTAssert(uploader, @"Target '%@' is missing an implementation", targetNumber);
59+
id<GDTPrioritizer> prioritizer = self->_registrar.targetToPrioritizer[targetNumber];
60+
id<GDTUploader> uploader = self->_registrar.targetToUploader[targetNumber];
61+
GDTAssert(prioritizer && uploader, @"Target '%@' is missing an implementation", targetNumber);
62+
GDTUploadConditions conds = [self uploadConditions];
63+
conds |= GDTUploadConditionHighPriority;
64+
GDTUploadPackage *package = [[prioritizer uploadPackageWithConditions:conds] copy];
65+
package.storage = self.storage;
66+
NSAssert(package.events && package.events.count,
67+
@"A high priority event should produce events to upload.");
68+
self->_targetToInFlightEventSet[targetNumber] = package.events;
6569
[uploader uploadPackage:package onComplete:self.onCompleteBlock];
66-
self->_targetToInFlightEventSet[targetNumber] = events;
70+
[self->_forcedUploadQueue removeLastObject];
6771
};
6872

6973
// Enqueue the force upload block if there's an in-flight upload for that target already.
@@ -112,9 +116,10 @@ - (GDTUploaderCompletionBlock)onCompleteBlock {
112116
GDTUploadCoordinatorForceUploadBlock queuedBlock =
113117
[strongSelf->_forcedUploadQueue lastObject];
114118
if (queuedBlock) {
115-
queuedBlock();
119+
dispatch_async(strongSelf->_coordinationQueue, ^{
120+
queuedBlock();
121+
});
116122
}
117-
[strongSelf->_forcedUploadQueue removeLastObject];
118123
}
119124
});
120125
}

GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTPrioritizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ typedef NS_OPTIONS(NSInteger, GDTUploadConditions) {
3232

3333
/** An upload would likely use wifi data. */
3434
GDTUploadConditionWifiData,
35+
36+
/** A high priority event has occurred. */
37+
GDTUploadConditionHighPriority,
3538
};
3639

3740
/** This protocol defines the common interface of event prioritization. Prioritizers are

GoogleDataTransport/Tests/Common/Fakes/GDTUploadCoordinatorFake.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
@implementation GDTUploadCoordinatorFake
2020

21-
- (void)forceUploadEvents:(NSSet<GDTStoredEvent *> *)events target:(GDTTarget)target {
21+
- (void)forceUploadForTarget:(GDTTarget)target {
2222
self.forceUploadCalled = YES;
2323
}
2424

GoogleDataTransport/Tests/Integration/GDTIntegrationTest.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ - (void)testEndToEndEvent {
152152
[testServer stop];
153153
}
154154

155-
/** Generates and events a bunch of random events. */
155+
/** Generates a bunch of random events. */
156156
- (void)generateEvents {
157157
for (int i = 0; i < 50; i++) {
158158
// Choose a random transport, and randomly choose if it's a telemetry event.

GoogleDataTransport/Tests/Unit/GDTUploadCoordinatorTest.m

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,16 @@ - (void)testSharedInstance {
7979

8080
/** Tests that forcing a event upload works. */
8181
- (void)testForceUploadEvents {
82+
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
83+
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
84+
self.prioritizer.uploadPackage = uploadPackage;
8285
XCTestExpectation *expectation = [self expectationWithDescription:@"uploader will upload"];
8386
self.uploader.uploadEventsBlock =
8487
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
8588
[expectation fulfill];
8689
};
87-
NSSet<GDTStoredEvent *> *eventSet = [GDTEventGenerator generate3StoredEvents];
88-
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadEvents:eventSet
89-
target:_target]);
90-
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
91-
[self waitForExpectations:@[ expectation ] timeout:0.1];
92-
});
90+
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:_target]);
91+
[self waitForExpectations:@[ expectation ] timeout:0.1];
9392
}
9493

9594
/** Tests forcing an upload while that target currently has a request in flight queues. */
@@ -101,21 +100,20 @@ - (void)testForceUploadEventsEnqueuesIftargetAlreadyHasEventsInFlight {
101100
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
102101
[expectation fulfill];
103102
};
104-
NSSet<GDTStoredEvent *> *eventSet = [GDTEventGenerator generate3StoredEvents];
103+
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
104+
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
105+
self.prioritizer.uploadPackage = uploadPackage;
105106
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
106107
[GDTUploadCoordinator sharedInstance].targetToInFlightEventSet[@(self->_target)] =
107108
[[NSSet alloc] init];
108109
});
109-
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadEvents:eventSet
110-
target:_target]);
110+
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:_target]);
111111
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
112112
XCTAssertEqual([GDTUploadCoordinator sharedInstance].forcedUploadQueue.count, 1);
113113
[GDTUploadCoordinator sharedInstance].onCompleteBlock(
114114
self.target, [GDTClock clockSnapshotInTheFuture:1000], nil);
115115
});
116-
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
117-
[self waitForExpectations:@[ expectation ] timeout:0.1];
118-
});
116+
[self waitForExpectations:@[ expectation ] timeout:1.0];
119117
}
120118

121119
/** Tests the timer is running at the desired frequency. */

0 commit comments

Comments
 (0)