Skip to content

Commit cee9c69

Browse files
authored
Improve performance of age limit trimming (#224)
* Improve performance of age limit trimming This does two things: 1. Before this patch, PINCache was naively scheduling a task to trim to age limit *recursively* every time the age limit was set. This makes it so recursive calls are canceled when it detects another recursive call has been kicked off. This is still less than ideal :/ 2. Trimming (mistakenly) used to be a high priority task. Now the age limit itself is set with a high priority, but the trimming is done at a low priority. * Update CHANGELOG * Explicitely retain self :/ * Should make tests more reliable * Fix wait until metadata is known in tests * Set internal modification immediately.
1 parent 1f88a1e commit cee9c69

6 files changed

+60
-29
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [performance] Reduce locking churn in cleanup methods. [#212](https://github.com/pinterest/PINCache/pull/212)
66
- [fix] Don't set file protection unless requested. [#220](https://github.com/pinterest/PINCache/pull/220)
77
- [new] Add ability to set an object level TTL: [#209](https://github.com/pinterest/PINCache/pull/209)
8+
- [performance] Improve performance of age limit trimming: [#224](https://github.com/pinterest/PINCache/pull/224)
89

910
## 3.0.1 -- Beta 6
1011
- [fix] Add some sane limits to the disk cache: [#201]https://github.com/pinterest/PINCache/pull/201

Source/PINDiskCache.m

+22-13
Original file line numberDiff line numberDiff line change
@@ -586,13 +586,6 @@ - (BOOL)_locked_setFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL
586586
error:&error];
587587
PINDiskCacheError(error);
588588

589-
if (success) {
590-
NSString *key = [self keyForEncodedFileURL:fileURL];
591-
if (key) {
592-
_metadata[key].lastModifiedDate = date;
593-
}
594-
}
595-
596589
return success;
597590
}
598591

@@ -785,13 +778,24 @@ - (void)trimToAgeLimitRecursively
785778
return;
786779

787780
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-ageLimit];
788-
[self trimDiskToDate:date];
781+
[self trimToDateAsync:date completion:nil];
789782

790783
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ageLimit * NSEC_PER_SEC));
791784
dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
792-
[self.operationQueue scheduleOperation:^{
793-
[self trimToAgeLimitRecursively];
794-
} withPriority:PINOperationQueuePriorityLow];
785+
// Ensure that ageLimit is the same as when we were scheduled, otherwise, we've been
786+
// rescheduled (another dispatch_after was issued) and should cancel.
787+
BOOL shouldReschedule = YES;
788+
[self lock];
789+
if (ageLimit != self->_ageLimit) {
790+
shouldReschedule = NO;
791+
}
792+
[self unlock];
793+
794+
if (shouldReschedule) {
795+
[self.operationQueue scheduleOperation:^{
796+
[self trimToAgeLimitRecursively];
797+
} withPriority:PINOperationQueuePriorityLow];
798+
}
795799
});
796800
}
797801

@@ -1059,8 +1063,10 @@ - (id)objectForKeyedSubscript:(NSString *)key
10591063
}
10601064
[self lock];
10611065
}
1062-
if (object)
1066+
if (object) {
1067+
_metadata[key].lastModifiedDate = now;
10631068
[self asynchronouslySetFileModificationDate:now forURL:fileURL];
1069+
}
10641070
}
10651071
[self unlock];
10661072

@@ -1090,6 +1096,7 @@ - (NSURL *)fileURLForKey:(NSString *)key updateFileModificationDate:(BOOL)update
10901096
[self lockForWriting];
10911097
if (fileURL.path && [[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
10921098
if (updateFileModificationDate) {
1099+
_metadata[key].lastModifiedDate = now;
10931100
[self asynchronouslySetFileModificationDate:now forURL:fileURL];
10941101
}
10951102
} else {
@@ -1506,7 +1513,9 @@ - (void)setAgeLimit:(NSTimeInterval)ageLimit
15061513
self->_ageLimit = ageLimit;
15071514
[self unlock];
15081515

1509-
[self trimToAgeLimitRecursively];
1516+
[self.operationQueue scheduleOperation:^{
1517+
[self trimToAgeLimitRecursively];
1518+
} withPriority:PINOperationQueuePriorityLow];
15101519
} withPriority:PINOperationQueuePriorityHigh];
15111520
}
15121521

Source/PINMemoryCache.m

+14-3
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,20 @@ - (void)trimToAgeLimitRecursively
298298

299299
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ageLimit * NSEC_PER_SEC));
300300
dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
301-
[self.operationQueue scheduleOperation:^{
302-
[self trimToAgeLimitRecursively];
303-
} withPriority:PINOperationQueuePriorityHigh];
301+
// Ensure that ageLimit is the same as when we were scheduled, otherwise, we've been
302+
// rescheduled (another dispatch_after was issued) and should cancel.
303+
BOOL shouldReschedule = YES;
304+
[self lock];
305+
if (ageLimit != self->_ageLimit) {
306+
shouldReschedule = NO;
307+
}
308+
[self unlock];
309+
310+
if (shouldReschedule) {
311+
[self.operationQueue scheduleOperation:^{
312+
[self trimToAgeLimitRecursively];
313+
} withPriority:PINOperationQueuePriorityHigh];
314+
}
304315
});
305316
}
306317

Tests/PINCacheTests.m

+2-2
Original file line numberDiff line numberDiff line change
@@ -1245,8 +1245,8 @@ - (void)testDiskRehydrationOfObjectAgeLimit
12451245
// Re-initialize the cache, this should read the age limit for the object from the extended file system attributes.
12461246
testCache = [[PINDiskCache alloc] initWithName:cacheName];
12471247
[testCache setTtlCacheSync:YES];
1248-
//This should not return until *after* disk cache directory has been created
1249-
[testCache setObject:@"some bogus object" forKey:@"some bogus key"];
1248+
1249+
[testCache waitForKnownState];
12501250
id object = testCache.metadata[key];
12511251
id ageLimitFromDisk = [object valueForKey:@"ageLimit"];
12521252
XCTAssertEqual([ageLimitFromDisk doubleValue], ageLimit);

Tests/PINDiskCache+PINCacheTests.h

+5
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,9 @@
88
*/
99
- (void)setTtlCacheSync:(BOOL)ttlCache;
1010

11+
/**
12+
Waits until all metadata has been read off the disk
13+
*/
14+
- (void)waitForKnownState;
15+
1116
@end

Tests/PINDiskCache+PINCacheTests.m

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
#import "PINDiskCache+PINCacheTests.h"
22

3-
@interface PINDiskCache ()
4-
- (void)setTtlCache:(BOOL)ttlCache;
3+
@interface PINDiskCache () {
4+
BOOL _ttlCache;
5+
}
6+
7+
- (void)lock;
8+
- (void)lockAndWaitForKnownState;
9+
- (void)unlock;
10+
511
@end
612

713
@implementation PINDiskCache (PINCacheTests)
814

915
- (void)setTtlCacheSync:(BOOL)ttlCache
1016
{
11-
[self setTtlCache:ttlCache];
17+
[self lock];
18+
self->_ttlCache = ttlCache;
19+
[self unlock];
20+
}
1221

13-
// Attempt to read from the cache. This will be put on the same queue as `setTtlCache`, but at a lower priority.
14-
// When the completion handler runs, we can be sure the property value has been set.
15-
dispatch_group_t group = dispatch_group_create();
16-
dispatch_group_enter(group);
17-
[self objectForKeyAsync:@"some bogus key" completion:^(PINDiskCache *cache, NSString *key, id<NSCoding> object) {
18-
dispatch_group_leave(group);
19-
}];
20-
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
22+
- (void)waitForKnownState
23+
{
24+
[self lockAndWaitForKnownState];
25+
[self unlock];
2126
}
2227

2328
@end

0 commit comments

Comments
 (0)