- SQLite3 in php — some notes
- APCu in php — some notes
- php’s microtime() function
I’ve been working on the SQLIite Object Cache plugin for WordPress. I’m using the APCu User Cache, php’s RAM cache subsystem, to accelerate cache lookups — specifically to support wp_cache_get() and wp_cache_get_multiple() operations. I’ve had to figure out a few things about this php feature.
Here are some notes.
Its documentation is not as comprehensive as some parts of php.
Installing
It is an optional php extension. It needs to be installed and/or activated in a php instance. On Ubuntu you can install it with
sudo apt install -y php8.2-apcu
mentioning your version of php in place of php8.2
. In a cPanel-controlled shared server, it can be chosen via a checkbox on the modules page of the Software -> php tab.
Don’t forget to restart your web server after adding this extension.
Is APCu enabled?
Use code like this to determine whether the APCu extension is installed and enabled.
$apcu_ok = function_exists( 'apcu_enabled' ) ? apcu_enabled() : false;
Organization, Size, Time-to-live
On an Ubuntu Linux machine, the cache goes into a single 32MiB shared memory segment, with about 4000 distinct cache slots. Each slot has a text-string name and an arbitary item of php data (string, scalar, array, or object) as a value. Each slot has a time-to-live (ttl) value,. When a new entry into the cache arrives and there’s no room for it, the APCu subsystem discards some entry with an expired ttl, or some aging entry. Under some circumstances the APCu cache gets flushed (gets all its enries deleted) when this happens.
When you place an item in the cache with an explicit Time-to-live (ttl) value, it vanishes at the appointed time. This is the same behavior as WordPress transients, which cannot be read after their ttls expire.
Features and limitations
It works as a shared-memory cache visible, between the various php worker processes working on behalf of a web server on a machine.
Unfortunately it does not work as a shared memory cache between php code running in command line context (for example in wp-cli or Drupal console) and code running on behalf of a web server. Without extra work this makes it unsuitable for supporting a persistent object cache for a hybrid web app with some command line work and some web server work.
It has a php.ini
configuration setting called apc.enable_cli. It’s best to leave that setting at its default of 0
, because the APCu that’s available to a CLI php program is not visible to the web server php programs or vice versa.
Avoid Key Collisions With Prefixes
A single APCu cache is shared among different applications running on the same web server. This is because the same pool of php worker threads serves the various applications.
So, it is vital to ensure that the cache keys on different applications don’t collide. If they do collide, one application will get information intended for another. This can wreak havoc (ask the author how he knows this sometime).
The best way to do this is by creating a unique key-name prefix for each application. When the applications are WordPress instances, this seems to be a good choice of unique key.
$prefix = substr( base64_encode(
md5( $table_prefix . DB_HOST . DB_USER . DB_NAME . AUTH_KEY . AUTH_SALT )
), 0, 12) . '.';
It takes about a microsecond to generate a 12-character randomized prefix by hashing the database access information and a couple of the random strings in wp-config.ini
. (Notice that creating a staging site sometimes does not change AUTH_KEY
and AUTH_SALT
so using the database is necessary.
Flushing the cache
You can flush the cache (delete all the entries) with apcu_clear_cache(). But, that deletes all the cached information, including that from other web apps or WordPress instances besides your own. This means two things:
- Applications can disrupt each others’ caches.
- Applications must be coded to be resilient to missing entries in the cache.
A good practice is only to delete the entries with your application’s name prefix when there is a need to flush your cache. Code like this will do that efficiently using apcu_delete().
$prefix = // Your app prefix
foreach ( new APCUIterator( '/^' . $prefix . '/', APC_ITER_KEY ) as $item ) {
apcu_delete( $item['key'] );
}
Key Info
Use the apcu_key_info() function to return an array of integers with information about a particular cache key. If the key doesn’t exist in the cache, it returns null
. The array looks like this
array (
'hits' => 10, // Count of the times apcu_fetch retrieved this value.
'access_time' => 1739197489, // UNIX timestamp of most recent apcu_fetch.
'mtime' => 1739197300, // UNIX timestamp of most recent change to value.
'creation_time' => 1739197200, // UNIX timestamp of when the key was first created.
'deletion_time' => 1739197600, // UNIX timestamp of when the key was deleted. 0 means active.
'ttl' => 86400, // Time-to-live in seconds. 0 means default.
'refs' => 0 // Number of internal references to this key.
)
Notice that the ttl
item in this array is not a UNIX timestamp, but rather the value you gave upon creating or setting the key’s value.
When you give an explicit ttl
value when creating or setting a key, the value is never returned after the ttl
expires. So, it is safe for use with ephemeral objects like WordPress transients with expiration times.
Diagnostics and debugging
The APCu extension developers have made a file called apc.php. If you put it in your web server root directory, you can use it to examine the use of your APCu cache. You can get it here. Avoid putting this file on production web sites, as it can give cybercreeps a way to look at your cached data and possibly steal information you don’t want stolen.
Hey 🙂
Will the plugin be dependent on the APCu extension or will it still work without it?
It will function with or without the extension.
Doh! (smacks forehead). You are right. I will fix this.