Automattic\WooCommerce\Internal\BatchProcessing
BatchProcessingController::acquire_enqueued_processors_lock
Acquire the enqueued-processors lock by claiming a row in the options table.
The claim is an atomic INSERT (which fails if the row already exists, making it a mutex); if the row exists but its stored release time has already passed, a conditional UPDATE takes the stale lock over. Failure to claim is retried up to ENQUEUED_PROCESSORS_LOCK_ATTEMPTS times with a short delay before the caller falls back to its best-effort unguarded path. Modeled on WC_Install::create_lock().
Release times are stored as fixed-width, zero-padded-fraction strings (number_format( ..., 6 )) so the database compares and matches them as plain strings without a numeric cast; for the current epoch the integer part is a stable ten digits, so lexical order matches chronological order.
Method of the class: BatchProcessingController{}
No Hooks.
Returns
string|null. The stored release time when the lock was acquired (which must be passed to release_enqueued_processors_lock()), or null when it could not be acquired.
Usage
// private - for code of main (parent) class only $result = $this->acquire_enqueued_processors_lock(): ?string;
Changelog
| Since 11.0.0 | Introduced. |
BatchProcessingController::acquire_enqueued_processors_lock() BatchProcessingController::acquire enqueued processors lock code WC 11.1.2
private function acquire_enqueued_processors_lock(): ?string {
global $wpdb;
$attempts = max( 1, self::ENQUEUED_PROCESSORS_LOCK_ATTEMPTS );
$delay_micro = (int) ( ( self::ENQUEUED_PROCESSORS_LOCK_TTL / $attempts ) * 1_000_000 );
// A contended INSERT fails on the duplicate key by design, so silence wpdb error reporting to keep the
// expected-failure path from spamming the log in debug mode; restore the prior setting when done.
$suppress = $wpdb->suppress_errors( true );
try {
for ( $attempt = 0; $attempt < $attempts; $attempt++ ) {
$time = microtime( true );
$now = number_format( $time, 6, '.', '' );
$expiry = number_format( $time + self::ENQUEUED_PROCESSORS_LOCK_TTL, 6, '.', '' );
// The unique option_name key makes this INSERT a mutex: it fails when the lock row already exists.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$acquired = $wpdb->insert(
$wpdb->options,
array(
'option_name' => self::ENQUEUED_PROCESSORS_LOCK_OPTION,
'option_value' => $expiry,
'autoload' => 'no',
),
array( '%s', '%s', '%s' )
);
if ( ! $acquired ) {
/*
* The lock row exists; take it over only if its stored release time is already in the past.
* $wpdb->update() cannot express the "<" comparison, so this conditional takeover uses raw SQL.
* The table is a trusted internal identifier and every value is bound through a placeholder.
*/
$takeover = $wpdb->prepare(
"UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s AND option_value < %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$expiry,
self::ENQUEUED_PROCESSORS_LOCK_OPTION,
$now
);
if ( is_string( $takeover ) ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$acquired = $wpdb->query( $takeover );
}
}
if ( $acquired ) {
return $expiry;
}
if ( $attempt < $attempts - 1 && $delay_micro > 0 ) {
usleep( $delay_micro );
}
}
return null;
} finally {
$wpdb->suppress_errors( $suppress );
}
}