I'm using APC for storing a map of class names to class file paths. I build the map like this in my autoload function:
$class_paths = apc_fetch('class_paths');
// If the class path is stored in application cache - search finished.
if (isset($class_paths[$class])) {
return require_once $class_paths[$class];
// Otherwise search in known places
} else {
// List of places to look for class
$paths = array(
'/src/',
'/modules/',
'/libs/',
);
// Search directories and store path in cache if found.
foreach ($paths as $path) {
$file = DOC_ROOT . $path . $class . '.php';
if (file_exists($file)) {
echo 'File was found in => ' . $file . '<br />';
$class_paths[$class] = $file;
apc_store('class_paths', $class_paths);
return require_once $file;
}
}
}
I can see as more and more classes are loaded, they are added to the map, but at some point the apc_fetch
returns NULL
in the middle of a page request, instead of returning the map.
Getting => class_paths
Array
(
[MCS\CMS\Helper\LayoutHelper] => /Users/mbl/Documents/Projects/mcs_ibob/core/trunk/src/MCS/CMS/Helper/LayoutHelper.php
[MCS\CMS\Model\Spot] => /Users/mbl/Documents/Projects/mcs_ibob/core/trunk/src/MCS/CMS/Model/Spot.php
)
Getting => class_paths
{null}
Many times the cached value will also be gone between page requests.
What could be the reason for this?
I'm using APC as an extension (PECL) running PHP 5.3.
UPDATE: In the comments below you will see people stating that APC is not persistent and that it is not to be trusted. But in my case the code is executed in one page request between 15-50ms. Shouldn't I be able to trust the APC for that long?
UPDATE:
It seems that the cache contains multiple entries with the same key, when it should only contain one - that is overwritten the value when invoking apc_store()
. I hope this can help someone understand the problem. (I've disabled slam defense and write lock)
Array
(
[num_slots] => 4099
[ttl] => 0
[num_hits] => 0
[num_misses] => 3
[num_inserts] => 9678
[expunges] => 0
[start_time] => 1293109072
[mem_size] => 40064
[num_entries] => 8
[file_upload_progress] => 1
[memory_type] => mmap
[locking_type] => file
[cache_list] => Array
(
[0] => Array
(
[info] => fSchema::mysql::fORM::default::/Users/mbl/Documents/Projects/mcs_ibob/core/trunk/public_html/::::column_info
[ttl] => 0
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 12456
)
[1] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[2] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[3] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[4] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[5] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[6] => Array
(
[info] => mcs:odk:class_paths
[ttl] => 3600
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 648
)
[7] => Array
(
[info] => fSchema::mysql::fORM::default::/Users/mbl/Documents/Projects/mcs_ibob/core/trunk/public_html/::::merged_column_info
[ttl] => 0
[type] => user
[num_hits] => 0
[mtime] => 1293109072
[creation_time] => 1293109072
[deletion_time] => 0
[access_time] => 1293109072
[ref_count] => 0
[mem_size] => 23720
)
)
[deleted_list] => Array
(
)
)
APC IS persistent but separate for each apache process.
PHP mentains a separate APC CACHE storage for each apache process, which is perfectly normal because each apache process has his own memory space.
I will try to answer my own question from what I've learned.
Firstly like many people has rightly pointed out.
It was for me hard to accept this statement without a practical explanation or even better a guide on how to work with it.
I've come to find that there are some rules or rather pitfalls to be aware of - many still eludes me. Using the APC API means using
apc_fetch
andapc_store
both of these can fail, or as it is more correctly put, miss. And why you sometimes receiveNULL
when you expected data is where the mystery lies.Here are some random bits of information:
Play around with
apc.ttl
andapc.user_ttl
till you find something that suits you - until you see lots of hits and not to many entries.apc_fetch
andapc_store
can returnNULL
for a number of reasons, some hinted above, such as ttl another reason can be a key fetch timeout. But the point is design-around it. Expect this.If you like me have been feeling very frustated about APC's behaviour have a look at this article pointed out to me by scoates: http://phpadvent.org/2010/share-and-enjoy-by-gopal-vijayaraghavan . It will not give you much answers but some basic knowledge about cache design and perhaps like me some relief in the fact I'm not alone :)
In my case with the class-path issue described in my question, I decided to use a local variable to build up the cache and then at the end storing it in the cache with one single
apc_store
invocation. This brought the hits way up and the cache entries per key way down - and my code is now running fast.It's not persistent storage. You may improve it by increasing memory size of APC but anyway, there are no warranties. So you should reinit values if you see they are dropped.