NSURLCache has inconsistent behavior if memoryCapacity != diskCapacity

Originator:zachlipton
Number:rdar://11877993 Date Originated:7/15/2012
Status:Open Resolved:
Product:iPad SDK Product Version:5.1
Classification:Serious Bug Reproducible:Always
 
15-Jul-2012 09:19 PM Zach Lipton:
Summary: 
After several days of screaming my head off at NSURLCache on iOS, I finally figured out what was going on. The problem was that responses were being cached properly, but subsequent requests were only sometimes being served from the cached copies. It was practically random: sometimes a request was immediately served from the cache, while other times NSURLConnection was blindly making a new request to the server even though I could see the perfectly good cached response in the SQLite cache file on disk.

I tried pretty much every permutation of configurations and debugging methods, and finally hit on the cause. NSURLCache doesn't work as expected if the memory capacity is not equal to the disk capacity. You see, I had initialized my cache with a large disk capacity and a smaller in-memory capacity, expecting to be able to benefit from the larger disk cache for storing large images on the retina iPad while keeping fast performance and reducing file IO with the memory cache:

[NSURLCache setSharedURLCache:[[MyCustomCache alloc] initWithMemoryCapacity:8 * 1024 * 1024 //8mb 
                                                                    diskCapacity:32 * 1024 * 1024 // 32mb 
                                                                        diskPath:@"webcache.db"]];

It appears that NSURLCache on iOS doesn't actually support this though. All the responses are written to the cache file as expected, but requests only hit the cache if the relevant response happens to be available in the in-memory portion of the cache. Otherwise, the Foundation implementation of cachedResponseForRequest: just returns nil instead of checking the disk cache as expected. 

Steps to Reproduce:
1. Run the attached code sample for an example. This sample uses a 1mb memory cache and 8mb disk cache, which makes the problem pretty obvious with a small number of requests. 
2. Change the memory and disk capacities (lines 32 and 33) to both be 1024*1024*8. 

Expected Results:
All responses are returned from the cache pursuant to the Cache-Control HTTP headers and standard caching heuristics.

Actual Results:
[NSURLCache cachedResponseForRequest:] alternates between blocks of returning results from the cache and returning nil. After changing the capacities to be equal, all responses are served from the cache as expected.

Regression:
iOS4 and earlier behavior was to ignore the disk cache entirely, as NSURLCache on iOS only stored results in memory (without really documenting this fact). It's certainly an improvement to have the disk cache working on iOS5, but if the class is not going to support using the memory and disk caches as a single multi-level cache (as one would expect), the API should enforce the requirement that both caches be the same size. 

Notes:
Feel free to contact me if I can provide further information. Thanks.

Comments

this did not work for me...


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!