Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vitepress/toc_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
"items": [
{ "text": "Events", "link": "/core-libraries/events" },
{ "text": "Caching", "link": "/core-libraries/caching" },
{ "text": "Locking", "link": "/core-libraries/locking" },
{ "text": "Email", "link": "/core-libraries/email" },
{ "text": "Logging", "link": "/core-libraries/logging" },
{ "text": "Security", "link": "/core-libraries/security" },
Expand Down
1 change: 1 addition & 0 deletions docs/en/contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- [Error & Exception Handling](development/errors)
- [Events System](core-libraries/events)
- [Internationalization & Localization](core-libraries/internationalization-and-localization)
- [Locking](core-libraries/locking)
- [Logging](core-libraries/logging)
- [Modelless Forms](core-libraries/form)
- [Pagination](controllers/pagination)
Expand Down
186 changes: 186 additions & 0 deletions docs/en/core-libraries/locking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Locking

`class` Cake\Lock\**Lock**

Locking helps you coordinate access to shared resources across concurrent
requests, CLI commands, queue workers, or background jobs. Use locks when you
need to ensure that only one process performs a critical section at a time.

CakePHP provides the `Cake\Lock\Lock` facade together with pluggable lock
engines for Redis, Memcached, local files, and no-op/testing usage.

## Configuring Lock Engines

Lock engine configurations are typically defined in **config/app.php**:

```php
'Lock' => [
'default' => [
'className' => 'Redis',
'host' => '127.0.0.1',
'port' => 6379,
'prefix' => 'myapp_lock_',
'ttl' => 60,
],
],
```

You can also configure lock engines at runtime:

```php
use Cake\Lock\Lock;

Lock::setConfig('orders', [
'className' => 'Redis',
'host' => '127.0.0.1',
'port' => 6379,
'prefix' => 'orders_',
'ttl' => 30,
]);
```

Lock configurations use the same `className` conventions as other registry-based
CakePHP services:

```php
Lock::setConfig('default', ['className' => 'Redis']);
Lock::setConfig('default', ['className' => 'Cake\Lock\Engine\RedisLockEngine']);
```

If an engine cannot be initialized, CakePHP falls back to the noop
`NullLockEngine` and emits a warning.

## Acquiring and Releasing Locks

Use `Lock::acquire()` to attempt a non-blocking lock:

```php
use Cake\Lock\Lock;

$lock = Lock::acquire('invoice-' . $invoiceId, ttl: 60);
if ($lock === null) {
return;
}

try {
$this->Invoices->send($invoiceId);
} finally {
$lock->release();
}
```

The returned `AcquiredLock` object represents the held lock. You can:

- call `release()` to release it explicitly
- call `refresh()` to extend the TTL for long-running work
- rely on its best-effort destructor cleanup if the handle is dropped

Explicit release is still recommended for predictable behavior.

## Preferred Usage with `synchronized()`

In many cases, `Lock::synchronized()` is the simplest and safest API because it
guarantees prompt release:

```php
$result = Lock::synchronized(
'reports-daily',
function () {
return $this->Reports->buildDaily();
},
ttl: 120,
timeout: 10,
);
```

If the lock cannot be acquired before the timeout expires, `synchronized()`
returns `null`.

## Blocking Acquisition

Use `Lock::acquireBlocking()` when you want to wait for a lock to become
available:

```php
$lock = Lock::acquireBlocking(
'payment-' . $paymentId,
ttl: 60,
timeout: 10,
retryInterval: 100,
);

if ($lock === null) {
return;
}

try {
$this->Payments->capture($paymentId);
} finally {
$lock->release();
}
```

The `retryInterval` value is expressed in milliseconds.

## Inspecting and Managing Locks

CakePHP provides additional helper methods for lock lifecycle management:

```php
if (Lock::isLocked('imports-products')) {
return;
}

$lock = Lock::acquire('imports-products');

if ($lock) {
$lock->refresh(120);
$lock->release();
}

Lock::forceRelease('imports-products');
```

- `isLocked()` performs a point-in-time check using the underlying engine
- `refresh()` extends the lock TTL if the current owner still holds the lock
- `forceRelease()` bypasses ownership checks and should only be used for
administrative recovery flows

## Available Engines

CakePHP ships with the following lock engines:

- `Redis` Recommended for distributed systems and multi-node deployments
- `Memcached` Suitable when Memcached is already part of your infrastructure
- `File` Useful for single-server deployments using local filesystem locks
- `Null` Useful for tests, local development, and intentional no-op behavior

### Shared Engine Options

All lock engines support these common options:

- `prefix` Prefix added to lock keys
- `ttl` Default lock time-to-live in seconds

### RedisLockEngine Options

- `host` Redis server host
- `port` Redis server port
- `password` Redis password
- `database` Redis database index
- `timeout` Connection timeout
- `persistent` Whether to use persistent connections

### MemcachedLockEngine Options

- `servers` Array of Memcached servers
- `persistent` Persistent connection identifier

### FileLockEngine Options

- `path` Directory used to store lock files

The `FileLockEngine` is local to a single host. Unlike Redis or Memcached, it
does not provide true TTL-based expiration in the backend. Locks are normally
released explicitly, when the lock handle is destroyed, or when the process
terminates.
1 change: 1 addition & 0 deletions docs/en/epub-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [Views](views)
- [Database Access & ORM](orm)
- [Caching](core-libraries/caching)
- [Locking](core-libraries/locking)
- [Bake Console](bake)
- [Console Commands](console-commands)
- [Debugging](development/debugging)
Expand Down
2 changes: 2 additions & 0 deletions docs/en/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ features in CakePHP are:

- A [caching](core-libraries/caching) framework that integrates with
Memcached, Redis and other backends.
- A [locking](core-libraries/locking) API for coordinating critical sections
across concurrent requests, workers, and commands.
- Powerful [code generation tools](bake/usage) so you can start immediately.
- [Integrated testing framework](development/testing) so you can ensure
your code works perfectly.
Expand Down
1 change: 1 addition & 0 deletions docs/en/pdf-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [Views](views)
- [Database Access & ORM](orm)
- [Caching](core-libraries/caching)
- [Locking](core-libraries/locking)
- [Bake Console](bake)
- [Console Commands](console-commands)
- [Debugging](development/debugging)
Expand Down
1 change: 1 addition & 0 deletions docs/en/topics.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Introduction to all the key parts of CakePHP:
- [Entities](orm/entities)
- [Error & Exception Handling](development/errors)
- [Caching](core-libraries/caching)
- [Locking](core-libraries/locking)
- [Logging](core-libraries/logging)
- [Modelless Forms](core-libraries/form)
- [Sessions](development/sessions)
Expand Down