Cache Stampede Lock
Creates a short-lived lock so only one request rebuilds an expired cache item while others use stale data or wait.
Function signature
ogAcquireCacheStampedeLock(cache_key, existing_lock = array(), ttl_seconds = 60, owner = '')
Categories
- Maintenance and Cron
Parameters
cache_keyCanonical cache key or cache subject being protected.existing_lockExisting lock record from shared storage or caller-owned state.ttl_secondsMaximum lock lifetime in seconds before a stale lock can be reviewed.ownerLog-safe owner label for the process or request attempting the lock.Return value
Public-safe status string returned by the function for explicit controller branching or logging.
- success
- message
- data
Compatibility
Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.
Minimum PHP version: 7.4
Security notes
Use caller-owned allowlists and context-specific escaping; validate admin actions, export fields, privacy plans, cache keys, templates, settings, routes, and ecommerce policies before production use.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Determines whether a cache rebuild lock may be acquired for a cache key.
*
* The caller must persist the returned lock token atomically in its chosen storage.
* This function does not pretend to be atomic by itself.
*
* @param string $cache_key Cache key being rebuilt.
* @param array $existing_lock Existing lock data with token and expires_at.
* @param int $ttl_seconds Lock lifetime in seconds.
* @param string $owner Optional owner label.
* @return array Lock decision and token data.
*/
function ogAcquireCacheStampedeLock($cache_key, $existing_lock = array(), $ttl_seconds = 60, $owner = '') {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$cache_key = trim((string)$cache_key);
$ttl_seconds = (int)$ttl_seconds;
$owner = trim((string)$owner);
if (empty($cache_key)) {
$result['message'] = 'Cache key is required.';
return $result;
}
if ($ttl_seconds < 5) {
$ttl_seconds = 5;
}
if (!is_array($existing_lock)) {
$existing_lock = array();
}
$current_time = time();
$existing_expires = 0;
if (!empty($existing_lock['expires_at'])) {
$existing_expires = (int)$existing_lock['expires_at'];
}
if ($existing_expires > $current_time) {
$result['success'] = true;
$result['message'] = 'Existing cache rebuild lock is still active.';
$result['data'] = array(
'acquired' => false,
'use_stale' => true,
'retry_after' => $existing_expires - $current_time,
'expires_at' => $existing_expires
);
return $result;
}
$token = bin2hex(random_bytes(16));
$result['success'] = true;
$result['message'] = 'Cache rebuild lock may be acquired.';
$result['data'] = array(
'acquired' => true,
'use_stale' => false,
'cache_key' => $cache_key,
'lock_token' => $token,
'owner' => $owner,
'expires_at' => $current_time + $ttl_seconds
);
return $result;
}