Foundation locks prevent two processes from performing the same protected work at the same time. Every implementation uses the shared Lock contract and returns an expiring LockToken that proves ownership.
Locks are useful when processing renewals, synchronizing a remote catalog, rebuilding a shared resource, or running any operation that must not overlap for the same record.
The backend examples below assume the application already has one composition root and registers feature providers through App. Review these guides before adding a lock provider:
The database implementation is the simplest persistent option when every process can reach the same primary WordPress database. Its guide covers provider wiring, lock-table initialization, and database-specific operating constraints.
Configure Database LockSelect DatabaseLock and initialize its WordPress table during deployment.
Redis is appropriate when requests and workers coordinate across several application servers. Install one supported client in addition to the Redis lock package:
composer require "predis/predis:>=3.0 <4.0"
Alternatively, install and enable the PhpRedis extension.
Map the connection and key prefix in the root config.php:
Redis Cluster supports only database 0, so it requires endpoint and key-prefix isolation. Foundation currently supports one writable Redis endpoint; Redis Cluster and Sentinel are not supported.
Application services should depend on Lock, not a backend class. For example, create src/Catalog/Catalog_Synchronizer.php and include the resource identifier in its lock name so unrelated work can proceed concurrently:
Catalog_Synchronizer.php
<?php declare(strict_types=1);namespace YourPlugin\Catalog;use RuntimeException;use StellarWP\Foundation\Lock\Contracts\Lock;use Throwable;/** * Prevents overlapping catalog synchronization for the same site. */final readonly class Catalog_Synchronizer { public function __construct( private Lock $lock ) { } /** * @param callable(): void $synchronize * * @throws RuntimeException When ownership cannot be confirmed during release. * @throws Throwable When synchronization or the lock backend fails. */ public function synchronize( int $site_id, callable $synchronize ): bool { $token = $this->lock->acquire( sprintf( 'catalog:%d:sync', $site_id ), 300 ); if ( $token === null ) { return false; } try { $synchronize(); } catch ( Throwable $failure ) { try { $this->lock->release( $token ); } catch ( Throwable ) { // Preserve the synchronization failure when cleanup also fails. } throw $failure; } if ( ! $this->lock->release( $token ) ) { throw new RuntimeException( 'Catalog synchronization lock ownership was lost.' ); } return true; }}
A false result means another process owns that site’s lock. The caller can skip the duplicate request, retry later, or enqueue it without blocking synchronization for other sites.