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
(
)
)
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_fetchandapc_storeboth of these can fail, or as it is more correctly put, miss. And why you sometimes receiveNULLwhen you expected data is where the mystery lies.Here are some random bits of information:
Play around with
apc.ttlandapc.user_ttltill you find something that suits you – until you see lots of hits and not to many entries.apc_fetchandapc_storecan returnNULLfor 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_storeinvocation. This brought the hits way up and the cache entries per key way down – and my code is now running fast.