Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify API by removing currentTime #25

Merged
merged 5 commits into from Feb 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Expand Up @@ -61,7 +61,6 @@ It is best to use different caches for different types of data you want to store
```objc
SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWithCachePath:cachePath
identifier:@"com.spotify.demo.image.cache"
currentTimeCallback:nil
defaultExpirationInterval:(60 * 60 * 24 * 30)
garbageCollectorInterval:(NSUInteger)(1.5 * SPTPersistentCacheDefaultGCIntervalSec)
debug:^(NSString *string) {
Expand All @@ -70,7 +69,6 @@ SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWith
options.sizeConstraintBytes = 1024 * 1024 * 10; // 10 MiB
SPTPersistentCache *cache = [[SPTPersistentCache alloc] initWithOptions:options];
```
Note that in the above example, the `currentTimeCallback` is `nil`. When this is nil it will default to using `NSDate` for its current time.

### Storing Data in the SPTPersistentCache
When storing data in the `SPTPersistentCache`, you must be aware of the file system semantics. The key will be used as the file name within the cache directory to save. The reason we did not implement a hash function under the hood is because we wanted to give the option of what hash function to use to the user, so it is recommended that when you insert data into the cache for a key, that you create the key using your own hashing function (at Spotify we use SHA1, although better hashing functions exist these days). If you want the cache record, i.e. file, to exist without any TTL make sure you store it as a locked file.
Expand Down
Expand Up @@ -45,7 +45,6 @@ - (void)viewDidLoad

SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWithCachePath:cachePath
identifier:@"com.spotify.demo.image.cache"
currentTimeCallback:nil
defaultExpirationInterval:(60 * 60 * 24 * 30)
garbageCollectorInterval:(NSUInteger)(1.5 * SPTPersistentCacheDefaultGCIntervalSec)
debug:^(NSString *string) { NSLog(@"%@", string); }];
Expand Down
17 changes: 11 additions & 6 deletions Sources/SPTPersistentCache.m
Expand Up @@ -55,8 +55,8 @@ @interface SPTPersistentCache ()
@property (nonatomic, strong) NSFileManager *fileManager;
@property (nonatomic, strong) NSTimer *gcTimer;
@property (nonatomic, copy) SPTPersistentCacheDebugCallback debugOutput;
@property (nonatomic, copy) SPTPersistentCacheCurrentTimeSecCallback currentTime;
@property (nonatomic, strong) SPTPersistentCacheFileManager *dataCacheFileManager;
@property (nonatomic, readonly) NSTimeInterval currentDateTimeInterval;
@property (nonatomic, strong) SPTPersistentCachePosixWrapper *posixWrapper;

@end
Expand All @@ -82,7 +82,6 @@ - (instancetype)initWithOptions:(SPTPersistentCacheOptions *)options
assert(_workQueue != nil);
self.fileManager = [NSFileManager defaultManager];

_currentTime = [self.options.currentTimeSec copy];
_debugOutput = [self.options.debugOutput copy];

_dataCacheFileManager = [[SPTPersistentCacheFileManager alloc] initWithOptions:_options];
Expand Down Expand Up @@ -234,6 +233,7 @@ - (void)touchDataForKey:(NSString *)key
NSString *filePath = [self.dataCacheFileManager pathForKey:key];

BOOL __block expired = NO;

SPTPersistentCacheResponse *response = [self alterHeaderForFileAtPath:filePath
withBlock:^(SPTPersistentCacheRecordHeader *header) {
// Satisfy Req.#1.2 and Req.#1.3
Expand All @@ -243,7 +243,7 @@ - (void)touchDataForKey:(NSString *)key
}
// Touch files that have default expiration policy
if (header->ttl == 0) {
header->updateTimeSec = spt_uint64rint(self.currentTime());
header->updateTimeSec = spt_uint64rint(self.currentDateTimeInterval);
}
}
writeBack:YES
Expand Down Expand Up @@ -538,7 +538,7 @@ - (void)loadDataForKeySync:(NSString *)key
record:record];
// If data ttl == 0 we update access time
if (ttl == 0) {
localHeader.updateTimeSec = spt_uint64rint(self.currentTime());
localHeader.updateTimeSec = spt_uint64rint(self.currentDateTimeInterval);
localHeader.crc = SPTPersistentCacheCalculateHeaderCRC(&localHeader);
memcpy(header, &localHeader, sizeof(localHeader));

Expand Down Expand Up @@ -584,7 +584,7 @@ - (NSError *)storeDataSync:(NSData *)data

SPTPersistentCacheRecordHeader header = SPTPersistentCacheRecordHeaderMake(ttl,
payloadLength,
spt_uint64rint(self.currentTime()),
spt_uint64rint(self.currentDateTimeInterval),
isLocked);

[rawData appendBytes:&header length:SPTPersistentCacheRecordHeaderSize];
Expand Down Expand Up @@ -757,7 +757,7 @@ - (BOOL)isDataExpiredWithHeader:(SPTPersistentCacheRecordHeader *)header
{
assert(header != nil);
uint64_t ttl = header->ttl;
uint64_t current = spt_uint64rint(self.currentTime());
uint64_t current = spt_uint64rint(self.currentDateTimeInterval);
int64_t threshold = (int64_t)((ttl > 0) ? ttl : self.options.defaultExpirationPeriodSec);

if (ttl > kTTLUpperBoundInSec) {
Expand Down Expand Up @@ -989,6 +989,11 @@ - (NSMutableArray *)storedImageNamesAndAttributes
return [sortedImages mutableCopy];
}

- (NSTimeInterval)currentDateTimeInterval
{
return [[NSDate date] timeIntervalSince1970];
}

#pragma mark SPTPersistentCache

- (void)dispatchBlock:(dispatch_block_t)block on:(dispatch_queue_t)queue
Expand Down
14 changes: 0 additions & 14 deletions Sources/SPTPersistentCacheOptions.m
Expand Up @@ -35,10 +35,7 @@
#pragma mark SPTPersistentCacheOptions

@interface SPTPersistentCacheOptions ()

@property (nonatomic) NSString *identifierForQueue;
@property (nonatomic, copy) SPTPersistentCacheCurrentTimeSecCallback currentTimeSec;

@end


Expand All @@ -50,26 +47,22 @@ - (instancetype)init
{
return [self initWithCachePath:nil
identifier:nil
currentTimeCallback:nil
debug:nil];
}

- (instancetype)initWithCachePath:(NSString *)cachePath
identifier:(NSString *)cacheIdentifier
currentTimeCallback:(SPTPersistentCacheCurrentTimeSecCallback)currentTimeBlock
debug:(SPTPersistentCacheDebugCallback)debugCallback
{
return [self initWithCachePath:cachePath
identifier:cacheIdentifier
currentTimeCallback:nil
defaultExpirationInterval:SPTPersistentCacheDefaultExpirationTimeSec
garbageCollectorInterval:SPTPersistentCacheDefaultGCIntervalSec
debug:nil];
}

- (instancetype)initWithCachePath:(NSString *)cachePath
identifier:(NSString *)cacheIdentifier
currentTimeCallback:(SPTPersistentCacheCurrentTimeSecCallback)currentTimeBlock
defaultExpirationInterval:(NSUInteger)defaultExpirationInterval
garbageCollectorInterval:(NSUInteger)garbageCollectorInterval
debug:(SPTPersistentCacheDebugCallback)debugCallback
Expand All @@ -93,13 +86,6 @@ - (instancetype)initWithCachePath:(NSString *)cachePath
_folderSeparationEnabled = YES;

_debugOutput = [debugCallback copy];
_currentTimeSec = currentTimeBlock ?: ^NSTimeInterval() {
return 0;
};

_currentTimeSec = (id)currentTimeBlock ?: [^() {
return [[NSDate date] timeIntervalSince1970];
} copy];

if (defaultExpirationInterval < SPTPersistentCacheMinimumExpirationLimit) {
SPTPersistentCacheOptionsDebug([NSString stringWithFormat:@"PersistentDataCache: Forcing defaultExpirationPeriodSec to %lu sec", (unsigned long)SPTPersistentCacheMinimumExpirationLimit],
Expand Down
1 change: 0 additions & 1 deletion Tests/SPTPersistentCacheFileManagerTests.m
Expand Up @@ -38,7 +38,6 @@ - (void)setUp

self.options = [[SPTPersistentCacheOptions alloc] initWithCachePath:SPTPersistentCacheFileManagerTestsCachePath
identifier:nil
currentTimeCallback:nil
debug:nil];

self.cacheFileManager = [[SPTPersistentCacheFileManager alloc] initWithOptions:self.options];
Expand Down
2 changes: 0 additions & 2 deletions Tests/SPTPersistentCacheOptionsTests.m
Expand Up @@ -51,7 +51,6 @@ - (void)testMinimumGarbageColectorInterval
{
SPTPersistentCacheOptions *dataCacheOptions = [[SPTPersistentCacheOptions alloc] initWithCachePath:nil
identifier:nil
currentTimeCallback:nil
defaultExpirationInterval:1
garbageCollectorInterval:1
debug:nil];
Expand All @@ -63,7 +62,6 @@ - (void)testMinimumDefaultExpirationInterval
{
SPTPersistentCacheOptions *dataCacheOptions = [[SPTPersistentCacheOptions alloc] initWithCachePath:nil
identifier:nil
currentTimeCallback:nil
defaultExpirationInterval:1
garbageCollectorInterval:1
debug:nil];
Expand Down
59 changes: 37 additions & 22 deletions Tests/SPTPersistentCacheTests.m
Expand Up @@ -114,17 +114,35 @@ @interface SPTPersistentCache (Testing)

@property (nonatomic, strong) dispatch_queue_t workQueue;
@property (nonatomic, strong) NSFileManager *fileManager;
@property (nonatomic, strong) NSTimer *gcTimer;

@property (nonatomic, copy) SPTPersistentCacheCurrentTimeSecCallback currentTime;
@property (nonatomic, strong) SPTPersistentCachePosixWrapper *posixWrapper;

- (NSTimeInterval)currentDateTimeInterval;
- (void)runRegularGC;
- (void)pruneBySize;

@end

@interface SPTPersistentCacheForUnitTests : SPTPersistentCache
@property (nonatomic, copy) SPTPersistentCacheCurrentTimeSecCallback timeIntervalCallback;
@end

@implementation SPTPersistentCacheForUnitTests

- (NSTimeInterval)currentDateTimeInterval
{
if (self.timeIntervalCallback) {
return self.timeIntervalCallback();
} else {
return [super currentDateTimeInterval];
}
}

@end


@interface SPTPersistentCacheTests : XCTestCase
@property (nonatomic, strong) SPTPersistentCache *cache;
@property (nonatomic, strong) SPTPersistentCacheForUnitTests *cache;
@property (nonatomic, strong) NSMutableArray *imageNames;
@property (nonatomic, strong) NSString *cachePath;
@property (nonatomic, strong) NSBundle *thisBundle;
Expand Down Expand Up @@ -1067,7 +1085,6 @@ - (void)testPruneWithSizeRestriction

SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWithCachePath:self.cachePath
identifier:nil
currentTimeCallback:nil
defaultExpirationInterval:SPTPersistentCacheDefaultExpirationTimeSec
garbageCollectorInterval:SPTPersistentCacheDefaultGCIntervalSec
debug:^(NSString *str) {
Expand Down Expand Up @@ -1164,7 +1181,6 @@ - (void)testInitNilWhenCannotCreateCacheDirectory
{
SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWithCachePath:nil
identifier:nil
currentTimeCallback:nil
debug:nil];

Method originalMethod = class_getClassMethod(NSFileManager.class, @selector(defaultManager));
Expand Down Expand Up @@ -1250,7 +1266,7 @@ - (void)testTouchDataWithExpiredHeader
}
NSString *key = self.imageNames[i];
[self.cache unlockDataForKeys:@[key] callback:nil onQueue:nil];
self.cache.currentTime = ^ {
self.cache.timeIntervalCallback = ^ {
return kTestEpochTime * 100.0;
};
__block BOOL called = NO;
Expand All @@ -1270,7 +1286,7 @@ - (void)testLockDataWithExpiredHeader
}
NSString *key = self.imageNames[i];
[self.cache unlockDataForKeys:@[key] callback:nil onQueue:nil];
self.cache.currentTime = ^ {
self.cache.timeIntervalCallback = ^ {
return kTestEpochTime * 100.0;
};
__block BOOL called = NO;
Expand Down Expand Up @@ -1300,19 +1316,15 @@ - (void)testUnlockDataMoreTimesThanLocked
}
}

- (void)testScheduleGarbageCollection
{
[self.cache scheduleGarbageCollector];
XCTAssertNotNil(self.cache.gcTimer);
}

- (void)testUnscheduleGarbageCollection
- (void)testCurrentDataTimeInterval
{
[self.cache scheduleGarbageCollector];
[self.cache unscheduleGarbageCollector];
XCTAssertNil(self.cache.gcTimer);
SPTPersistentCache *cache = [self createCacheWithTimeCallback:nil expirationTime:0];

NSTimeInterval firstTimeInterval = [cache currentDateTimeInterval];
NSTimeInterval secondTimeInterval = [cache currentDateTimeInterval];

XCTAssertGreaterThan(secondTimeInterval, firstTimeInterval);
}

- (void)testUnlockDataWithNoKeys
{
BOOL result = [self.cache unlockDataForKeys:nil callback:nil onQueue:nil];
Expand Down Expand Up @@ -1567,19 +1579,22 @@ - (void)alterUpdateTime:(uint64_t)updateTime forFileAtPath:(NSString *)filePath
close(fd);
}

- (SPTPersistentCache *)createCacheWithTimeCallback:(SPTPersistentCacheCurrentTimeSecCallback)currentTime
expirationTime:(NSTimeInterval)expirationTimeSec
- (SPTPersistentCacheForUnitTests *)createCacheWithTimeCallback:(SPTPersistentCacheCurrentTimeSecCallback)currentTime
expirationTime:(NSTimeInterval)expirationTimeSec
{
SPTPersistentCacheOptions *options = [[SPTPersistentCacheOptions alloc] initWithCachePath:self.cachePath
identifier:nil
currentTimeCallback:currentTime
defaultExpirationInterval:(NSUInteger)expirationTimeSec
garbageCollectorInterval:SPTPersistentCacheDefaultGCIntervalSec
debug:^(NSString *str) {
NSLog(@"%@", str);
}];


return [[SPTPersistentCache alloc] initWithOptions:options];
SPTPersistentCacheForUnitTests *cache = [[SPTPersistentCacheForUnitTests alloc] initWithOptions:options];
cache.timeIntervalCallback = currentTime;

return cache;
}

- (NSUInteger)getFilesNumberAtPath:(NSString *)path
Expand Down
8 changes: 4 additions & 4 deletions Tests/SPTPersistentCacheTimerProxyTests.m
Expand Up @@ -23,15 +23,15 @@
#import "SPTPersistentCacheTimerProxy.h"
#import <SPTPersistentCache/SPTPersistentCache.h>

@interface SPTPersistentCacheForUnitTests : SPTPersistentCache
@interface SPTPersistentCacheForTimerProxyUnitTests : SPTPersistentCache
@property (nonatomic) dispatch_queue_t queue;
@property (nonatomic) XCTestExpectation *testExpectation;
@property (nonatomic) BOOL wasCalledFromIncorrectQueue;
@property (nonatomic) BOOL wasRunRegularGCCalled;
@property (nonatomic) BOOL wasPruneBySizeCalled;
@end

@implementation SPTPersistentCacheForUnitTests
@implementation SPTPersistentCacheForTimerProxyUnitTests

- (void)runRegularGC
{
Expand Down Expand Up @@ -60,7 +60,7 @@ - (void)setUp
{
[super setUp];

self.dataCache = [[SPTPersistentCacheForUnitTests alloc] init];
self.dataCache = [[SPTPersistentCacheForTimerProxyUnitTests alloc] init];

self.dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

Expand All @@ -80,7 +80,7 @@ - (void)testGarbageCollectorEnqueue
{
XCTestExpectation *expectation = [self expectationWithDescription:@"testGarbageCollectorEnqueue"];

SPTPersistentCacheForUnitTests *dataCacheForUnitTests = (SPTPersistentCacheForUnitTests *)self.timerProxy.dataCache;
SPTPersistentCacheForTimerProxyUnitTests *dataCacheForUnitTests = (SPTPersistentCacheForTimerProxyUnitTests *)self.timerProxy.dataCache;
dataCacheForUnitTests.queue = self.timerProxy.queue;
dataCacheForUnitTests.testExpectation = expectation;
[self.timerProxy enqueueGC:nil];
Expand Down