Current Location: Home> Latest Articles> Key-value conflict and solution in apcu_entry

Key-value conflict and solution in apcu_entry

gitbox 2025-05-17

In PHP's cache mechanism, apcu_entry is a very efficient function that can obtain or set a cache entry atomically. However, in high concurrency scenarios, if multiple processes or requests try to write apcu_entry to the same key at the same time, key-value conflicts may occur, which will lead to some potential problems, such as performance degradation, data inconsistency, and even cache pollution. This article will explore the causes of this situation in depth and provide several coping strategies.

1. Problem Overview

Typical usage of apcu_entry is as follows:

 $value = apcu_entry('my_cache_key', function() {
    // Calculate and return cached content
    return heavyComputation();
}, 300);

The purpose of the above code is to look for my_cache_key in the cache. If it does not exist, execute the callback function and store the result in the cache, with an effective period of 300 seconds. On the surface, this seems to be thread-safe, but the problem occurs in a high concurrency environment: when multiple requests have not yet established the key of APCu, they will enter the callback function at the same time, repeatedly perform the recalculation logic, and even lead to write conflicts.

2. Causes of key-value conflicts

APCu is a user-state cache of process shared memory. Although its operations are atomic, the callback itself is not exclusive. If a key is judged "at the same time" in multiple requests, each request will call the callback function and try to write, which may lead to a contender writing.

In addition, some older versions of APCu have bugs, and may even have segfaults or cache inconsistent edge cases under extreme concurrency pressure.

3. Response plan

1. Set unique key prefix in advance

Use a unified naming strategy to avoid accidental use of the same keys for different business logics. For example:

 $key = 'myapp_moduleX_' . md5($param);

This can significantly reduce the probability of conflict.

2. Use lock mechanism to wrap callbacks

"Exclusive initialization" can be achieved through file locks or APCu's own lock keys:

 $key = 'my_cache_key';
$lockKey = $key . '_lock';

if (!apcu_exists($key)) {
    $acquired = apcu_add($lockKey, 1, 5);
    if ($acquired) {
        // Obtain the lock,Execute a callback and write to the cache
        $value = heavyComputation();
        apcu_store($key, $value, 300);
        apcu_delete($lockKey);
    } else {
        // Wait for the cache to be available
        while (!apcu_exists($key)) {
            usleep(10000); // 10ms
        }
        $value = apcu_fetch($key);
    }
} else {
    $value = apcu_fetch($key);
}

This approach avoids multiple requests concurrently entering the computing logic, but also brings up wait time and implementation complexity.

3. Limit time-consuming operations in callbacks

The callback function itself should avoid long-term blocking operations, such as remote calls, database queries, etc. If this is required, the cache logic should be migrated to the application initialization process to avoid real-time dynamic writing. For example:

 $value = apcu_fetch('config_global');
if ($value === false) {
    $value = file_get_contents('https://gitbox.net/api/config/global.json');
    apcu_store('config_global', $value, 600);
}

4. Use delayed write caching policy

Cache the calculation results in local memory (such as static variables or request contexts) first, and write APCu to the end of the request, which can reduce competition for shared cache:

 static $localCache = [];

function getCachedData($key, $callback, $ttl = 300) {
    global $localCache;
    if (isset($localCache[$key])) {
        return $localCache[$key];
    }

    $value = apcu_fetch($key);
    if ($value === false) {
        $value = $callback();
        $localCache[$key] = $value;
        register_shutdown_function(function() use ($key, $value, $ttl) {
            apcu_store($key, $value, $ttl);
        });
    }
    return $value;
}

4. Summary

Although apcu_entry provides an elegant cache initialization mechanism, key-value conflicts in high concurrency still need to be handled by the developer. It is recommended to use explicit locks in critical business logic to control cache write behavior or avoid instant write cache operations during requests. In addition, reasonable key naming and cache hierarchy strategies can also effectively reduce the risk of conflict. The rational use of APCu can enable PHP applications to achieve a good balance between response speed and system pressure.