1218 lines
32 KiB
Markdown
1218 lines
32 KiB
Markdown
# Cache
|
||
|
||
* Introduction
|
||
* Configuration
|
||
* Driver Prerequisites
|
||
* Cache Usage
|
||
* Obtaining a Cache Instance
|
||
* Retrieving Items From the Cache
|
||
* Storing Items in the Cache
|
||
* Removing Items From the Cache
|
||
* Cache Memoization
|
||
* The Cache Helper
|
||
* Atomic Locks
|
||
* Managing Locks
|
||
* Managing Locks Across Processes
|
||
* Adding Custom Cache Drivers
|
||
* Writing the Driver
|
||
* Registering the Driver
|
||
* Events
|
||
|
||
## Introduction
|
||
|
||
Some of the data retrieval or processing tasks performed by your application
|
||
could be CPU intensive or take several seconds to complete. When this is the
|
||
case, it is common to cache the retrieved data for a time so it can be
|
||
retrieved quickly on subsequent requests for the same data. The cached data is
|
||
usually stored in a very fast data store such as
|
||
[Memcached](https://memcached.org) or [Redis](https://redis.io).
|
||
|
||
Thankfully, Laravel provides an expressive, unified API for various cache
|
||
backends, allowing you to take advantage of their blazing fast data retrieval
|
||
and speed up your web application.
|
||
|
||
## Configuration
|
||
|
||
Your application's cache configuration file is located at `config/cache.php`.
|
||
In this file, you may specify which cache store you would like to be used by
|
||
default throughout your application. Laravel supports popular caching backends
|
||
like [Memcached](https://memcached.org), [Redis](https://redis.io),
|
||
[DynamoDB](https://aws.amazon.com/dynamodb), and relational databases out of
|
||
the box. In addition, a file based cache driver is available, while `array`
|
||
and `null` cache drivers provide convenient cache backends for your automated
|
||
tests.
|
||
|
||
The cache configuration file also contains a variety of other options that you
|
||
may review. By default, Laravel is configured to use the `database` cache
|
||
driver, which stores the serialized, cached objects in your application's
|
||
database.
|
||
|
||
### Driver Prerequisites
|
||
|
||
#### Database
|
||
|
||
When using the `database` cache driver, you will need a database table to
|
||
contain the cache data. Typically, this is included in Laravel's default
|
||
`0001_01_01_000001_create_cache_table.php` [database
|
||
migration](/docs/12.x/migrations); however, if your application does not
|
||
contain this migration, you may use the `make:cache-table` Artisan command to
|
||
create it:
|
||
|
||
|
||
|
||
1php artisan make:cache-table
|
||
|
||
2
|
||
|
||
3php artisan migrate
|
||
|
||
|
||
php artisan make:cache-table
|
||
|
||
php artisan migrate
|
||
|
||
#### Memcached
|
||
|
||
Using the Memcached driver requires the [Memcached PECL
|
||
package](https://pecl.php.net/package/memcached) to be installed. You may list
|
||
all of your Memcached servers in the `config/cache.php` configuration file.
|
||
This file already contains a `memcached.servers` entry to get you started:
|
||
|
||
|
||
|
||
1'memcached' => [
|
||
|
||
2 // ...
|
||
|
||
3
|
||
|
||
4 'servers' => [
|
||
|
||
5 [
|
||
|
||
6 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||
|
||
7 'port' => env('MEMCACHED_PORT', 11211),
|
||
|
||
8 'weight' => 100,
|
||
|
||
9 ],
|
||
|
||
10 ],
|
||
|
||
11],
|
||
|
||
|
||
'memcached' => [
|
||
// ...
|
||
|
||
'servers' => [
|
||
[
|
||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||
'port' => env('MEMCACHED_PORT', 11211),
|
||
'weight' => 100,
|
||
],
|
||
],
|
||
],
|
||
|
||
If needed, you may set the `host` option to a UNIX socket path. If you do
|
||
this, the `port` option should be set to `0`:
|
||
|
||
|
||
|
||
1'memcached' => [
|
||
|
||
2 // ...
|
||
|
||
3
|
||
|
||
4 'servers' => [
|
||
|
||
5 [
|
||
|
||
6 'host' => '/var/run/memcached/memcached.sock',
|
||
|
||
7 'port' => 0,
|
||
|
||
8 'weight' => 100
|
||
|
||
9 ],
|
||
|
||
10 ],
|
||
|
||
11],
|
||
|
||
|
||
'memcached' => [
|
||
// ...
|
||
|
||
'servers' => [
|
||
[
|
||
'host' => '/var/run/memcached/memcached.sock',
|
||
'port' => 0,
|
||
'weight' => 100
|
||
],
|
||
],
|
||
],
|
||
|
||
#### Redis
|
||
|
||
Before using a Redis cache with Laravel, you will need to either install the
|
||
PhpRedis PHP extension via PECL or install the `predis/predis` package (~2.0)
|
||
via Composer. [Laravel Sail](/docs/12.x/sail) already includes this extension.
|
||
In addition, official Laravel application platforms such as [Laravel
|
||
Cloud](https://cloud.laravel.com) and [Laravel
|
||
Forge](https://forge.laravel.com) have the PhpRedis extension installed by
|
||
default.
|
||
|
||
For more information on configuring Redis, consult its [Laravel documentation
|
||
page](/docs/12.x/redis#configuration).
|
||
|
||
#### DynamoDB
|
||
|
||
Before using the [DynamoDB](https://aws.amazon.com/dynamodb) cache driver, you
|
||
must create a DynamoDB table to store all of the cached data. Typically, this
|
||
table should be named `cache`. However, you should name the table based on the
|
||
value of the `stores.dynamodb.table` configuration value within the `cache`
|
||
configuration file. The table name may also be set via the
|
||
`DYNAMODB_CACHE_TABLE` environment variable.
|
||
|
||
This table should also have a string partition key with a name that
|
||
corresponds to the value of the `stores.dynamodb.attributes.key` configuration
|
||
item within your application's `cache` configuration file. By default, the
|
||
partition key should be named `key`.
|
||
|
||
Typically, DynamoDB will not proactively remove expired items from a table.
|
||
Therefore, you should [enable Time to Live
|
||
(TTL)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)
|
||
on the table. When configuring the table's TTL settings, you should set the
|
||
TTL attribute name to `expires_at`.
|
||
|
||
Next, install the AWS SDK so that your Laravel application can communicate
|
||
with DynamoDB:
|
||
|
||
|
||
|
||
1composer require aws/aws-sdk-php
|
||
|
||
|
||
composer require aws/aws-sdk-php
|
||
|
||
In addition, you should ensure that values are provided for the DynamoDB cache
|
||
store configuration options. Typically these options, such as
|
||
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, should be defined in your
|
||
application's `.env` configuration file:
|
||
|
||
|
||
|
||
1'dynamodb' => [
|
||
|
||
2 'driver' => 'dynamodb',
|
||
|
||
3 'key' => env('AWS_ACCESS_KEY_ID'),
|
||
|
||
4 'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||
|
||
5 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||
|
||
6 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||
|
||
7 'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||
|
||
8],
|
||
|
||
|
||
'dynamodb' => [
|
||
'driver' => 'dynamodb',
|
||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||
],
|
||
|
||
#### MongoDB
|
||
|
||
If you are using MongoDB, a `mongodb` cache driver is provided by the official
|
||
`mongodb/laravel-mongodb` package and can be configured using a `mongodb`
|
||
database connection. MongoDB supports TTL indexes, which can be used to
|
||
automatically clear expired cache items.
|
||
|
||
For more information on configuring MongoDB, please refer to the MongoDB
|
||
[Cache and Locks
|
||
documentation](https://www.mongodb.com/docs/drivers/php/laravel-
|
||
mongodb/current/cache/).
|
||
|
||
## Cache Usage
|
||
|
||
### Obtaining a Cache Instance
|
||
|
||
To obtain a cache store instance, you may use the `Cache` facade, which is
|
||
what we will use throughout this documentation. The `Cache` facade provides
|
||
convenient, terse access to the underlying implementations of the Laravel
|
||
cache contracts:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Http\Controllers;
|
||
|
||
4
|
||
|
||
5use Illuminate\Support\Facades\Cache;
|
||
|
||
6
|
||
|
||
7class UserController extends Controller
|
||
|
||
8{
|
||
|
||
9 /**
|
||
|
||
10 * Show a list of all users of the application.
|
||
|
||
11 */
|
||
|
||
12 public function index(): array
|
||
|
||
13 {
|
||
|
||
14 $value = Cache::get('key');
|
||
|
||
15
|
||
|
||
16 return [
|
||
|
||
17 // ...
|
||
|
||
18 ];
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use Illuminate\Support\Facades\Cache;
|
||
|
||
class UserController extends Controller
|
||
{
|
||
/**
|
||
* Show a list of all users of the application.
|
||
*/
|
||
public function index(): array
|
||
{
|
||
$value = Cache::get('key');
|
||
|
||
return [
|
||
// ...
|
||
];
|
||
}
|
||
}
|
||
|
||
#### Accessing Multiple Cache Stores
|
||
|
||
Using the `Cache` facade, you may access various cache stores via the `store`
|
||
method. The key passed to the `store` method should correspond to one of the
|
||
stores listed in the `stores` configuration array in your `cache`
|
||
configuration file:
|
||
|
||
|
||
|
||
1$value = Cache::store('file')->get('foo');
|
||
|
||
2
|
||
|
||
3Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes
|
||
|
||
|
||
$value = Cache::store('file')->get('foo');
|
||
|
||
Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes
|
||
|
||
### Retrieving Items From the Cache
|
||
|
||
The `Cache` facade's `get` method is used to retrieve items from the cache. If
|
||
the item does not exist in the cache, `null` will be returned. If you wish,
|
||
you may pass a second argument to the `get` method specifying the default
|
||
value you wish to be returned if the item doesn't exist:
|
||
|
||
|
||
|
||
1$value = Cache::get('key');
|
||
|
||
2
|
||
|
||
3$value = Cache::get('key', 'default');
|
||
|
||
|
||
$value = Cache::get('key');
|
||
|
||
$value = Cache::get('key', 'default');
|
||
|
||
You may even pass a closure as the default value. The result of the closure
|
||
will be returned if the specified item does not exist in the cache. Passing a
|
||
closure allows you to defer the retrieval of default values from a database or
|
||
other external service:
|
||
|
||
|
||
|
||
1$value = Cache::get('key', function () {
|
||
|
||
2 return DB::table(/* ... */)->get();
|
||
|
||
3});
|
||
|
||
|
||
$value = Cache::get('key', function () {
|
||
return DB::table(/* ... */)->get();
|
||
});
|
||
|
||
#### Determining Item Existence
|
||
|
||
The `has` method may be used to determine if an item exists in the cache. This
|
||
method will also return `false` if the item exists but its value is `null`:
|
||
|
||
|
||
|
||
1if (Cache::has('key')) {
|
||
|
||
2 // ...
|
||
|
||
3}
|
||
|
||
|
||
if (Cache::has('key')) {
|
||
// ...
|
||
}
|
||
|
||
#### Incrementing / Decrementing Values
|
||
|
||
The `increment` and `decrement` methods may be used to adjust the value of
|
||
integer items in the cache. Both of these methods accept an optional second
|
||
argument indicating the amount by which to increment or decrement the item's
|
||
value:
|
||
|
||
|
||
|
||
1// Initialize the value if it does not exist...
|
||
|
||
2Cache::add('key', 0, now()->addHours(4));
|
||
|
||
3
|
||
|
||
4// Increment or decrement the value...
|
||
|
||
5Cache::increment('key');
|
||
|
||
6Cache::increment('key', $amount);
|
||
|
||
7Cache::decrement('key');
|
||
|
||
8Cache::decrement('key', $amount);
|
||
|
||
|
||
// Initialize the value if it does not exist...
|
||
Cache::add('key', 0, now()->addHours(4));
|
||
|
||
// Increment or decrement the value...
|
||
Cache::increment('key');
|
||
Cache::increment('key', $amount);
|
||
Cache::decrement('key');
|
||
Cache::decrement('key', $amount);
|
||
|
||
#### Retrieve and Store
|
||
|
||
Sometimes you may wish to retrieve an item from the cache, but also store a
|
||
default value if the requested item doesn't exist. For example, you may wish
|
||
to retrieve all users from the cache or, if they don't exist, retrieve them
|
||
from the database and add them to the cache. You may do this using the
|
||
`Cache::remember` method:
|
||
|
||
|
||
|
||
1$value = Cache::remember('users', $seconds, function () {
|
||
|
||
2 return DB::table('users')->get();
|
||
|
||
3});
|
||
|
||
|
||
$value = Cache::remember('users', $seconds, function () {
|
||
return DB::table('users')->get();
|
||
});
|
||
|
||
If the item does not exist in the cache, the closure passed to the `remember`
|
||
method will be executed and its result will be placed in the cache.
|
||
|
||
You may use the `rememberForever` method to retrieve an item from the cache or
|
||
store it forever if it does not exist:
|
||
|
||
|
||
|
||
1$value = Cache::rememberForever('users', function () {
|
||
|
||
2 return DB::table('users')->get();
|
||
|
||
3});
|
||
|
||
|
||
$value = Cache::rememberForever('users', function () {
|
||
return DB::table('users')->get();
|
||
});
|
||
|
||
#### Stale While Revalidate
|
||
|
||
When using the `Cache::remember` method, some users may experience slow
|
||
response times if the cached value has expired. For certain types of data, it
|
||
can be useful to allow partially stale data to be served while the cached
|
||
value is recalculated in the background, preventing some users from
|
||
experiencing slow response times while cached values are calculated. This is
|
||
often referred to as the "stale-while-revalidate" pattern, and the
|
||
`Cache::flexible` method provides an implementation of this pattern.
|
||
|
||
The flexible method accepts an array that specifies how long the cached value
|
||
is considered “fresh” and when it becomes “stale.” The first value in the
|
||
array represents the number of seconds the cache is considered fresh, while
|
||
the second value defines how long it can be served as stale data before
|
||
recalculation is necessary.
|
||
|
||
If a request is made within the fresh period (before the first value), the
|
||
cache is returned immediately without recalculation. If a request is made
|
||
during the stale period (between the two values), the stale value is served to
|
||
the user, and a [deferred function](/docs/12.x/helpers#deferred-functions) is
|
||
registered to refresh the cached value after the response is sent to the user.
|
||
If a request is made after the second value, the cache is considered expired,
|
||
and the value is recalculated immediately, which may result in a slower
|
||
response for the user:
|
||
|
||
|
||
|
||
1$value = Cache::flexible('users', [5, 10], function () {
|
||
|
||
2 return DB::table('users')->get();
|
||
|
||
3});
|
||
|
||
|
||
$value = Cache::flexible('users', [5, 10], function () {
|
||
return DB::table('users')->get();
|
||
});
|
||
|
||
#### Retrieve and Delete
|
||
|
||
If you need to retrieve an item from the cache and then delete the item, you
|
||
may use the `pull` method. Like the `get` method, `null` will be returned if
|
||
the item does not exist in the cache:
|
||
|
||
|
||
|
||
1$value = Cache::pull('key');
|
||
|
||
2
|
||
|
||
3$value = Cache::pull('key', 'default');
|
||
|
||
|
||
$value = Cache::pull('key');
|
||
|
||
$value = Cache::pull('key', 'default');
|
||
|
||
### Storing Items in the Cache
|
||
|
||
You may use the `put` method on the `Cache` facade to store items in the
|
||
cache:
|
||
|
||
|
||
|
||
1Cache::put('key', 'value', $seconds = 10);
|
||
|
||
|
||
Cache::put('key', 'value', $seconds = 10);
|
||
|
||
If the storage time is not passed to the `put` method, the item will be stored
|
||
indefinitely:
|
||
|
||
|
||
|
||
1Cache::put('key', 'value');
|
||
|
||
|
||
Cache::put('key', 'value');
|
||
|
||
Instead of passing the number of seconds as an integer, you may also pass a
|
||
`DateTime` instance representing the desired expiration time of the cached
|
||
item:
|
||
|
||
|
||
|
||
1Cache::put('key', 'value', now()->addMinutes(10));
|
||
|
||
|
||
Cache::put('key', 'value', now()->addMinutes(10));
|
||
|
||
#### Store if Not Present
|
||
|
||
The `add` method will only add the item to the cache if it does not already
|
||
exist in the cache store. The method will return `true` if the item is
|
||
actually added to the cache. Otherwise, the method will return `false`. The
|
||
`add` method is an atomic operation:
|
||
|
||
|
||
|
||
1Cache::add('key', 'value', $seconds);
|
||
|
||
|
||
Cache::add('key', 'value', $seconds);
|
||
|
||
#### Storing Items Forever
|
||
|
||
The `forever` method may be used to store an item in the cache permanently.
|
||
Since these items will not expire, they must be manually removed from the
|
||
cache using the `forget` method:
|
||
|
||
|
||
|
||
1Cache::forever('key', 'value');
|
||
|
||
|
||
Cache::forever('key', 'value');
|
||
|
||
If you are using the Memcached driver, items that are stored "forever" may be
|
||
removed when the cache reaches its size limit.
|
||
|
||
### Removing Items From the Cache
|
||
|
||
You may remove items from the cache using the `forget` method:
|
||
|
||
|
||
|
||
1Cache::forget('key');
|
||
|
||
|
||
Cache::forget('key');
|
||
|
||
You may also remove items by providing a zero or negative number of expiration
|
||
seconds:
|
||
|
||
|
||
|
||
1Cache::put('key', 'value', 0);
|
||
|
||
2
|
||
|
||
3Cache::put('key', 'value', -5);
|
||
|
||
|
||
Cache::put('key', 'value', 0);
|
||
|
||
Cache::put('key', 'value', -5);
|
||
|
||
You may clear the entire cache using the `flush` method:
|
||
|
||
|
||
|
||
1Cache::flush();
|
||
|
||
|
||
Cache::flush();
|
||
|
||
Flushing the cache does not respect your configured cache "prefix" and will
|
||
remove all entries from the cache. Consider this carefully when clearing a
|
||
cache which is shared by other applications.
|
||
|
||
### Cache Memoization
|
||
|
||
Laravel's `memo` cache driver allows you to temporarily store resolved cache
|
||
values in memory during a single request or job execution. This prevents
|
||
repeated cache hits within the same execution, significantly improving
|
||
performance.
|
||
|
||
To use the memoized cache, invoke the `memo` method:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Cache;
|
||
|
||
2
|
||
|
||
3$value = Cache::memo()->get('key');
|
||
|
||
|
||
use Illuminate\Support\Facades\Cache;
|
||
|
||
$value = Cache::memo()->get('key');
|
||
|
||
The `memo` method optionally accepts the name of a cache store, which
|
||
specifies the underlying cache store the memoized driver will decorate:
|
||
|
||
|
||
|
||
1// Using the default cache store...
|
||
|
||
2$value = Cache::memo()->get('key');
|
||
|
||
3
|
||
|
||
4// Using the Redis cache store...
|
||
|
||
5$value = Cache::memo('redis')->get('key');
|
||
|
||
|
||
// Using the default cache store...
|
||
$value = Cache::memo()->get('key');
|
||
|
||
// Using the Redis cache store...
|
||
$value = Cache::memo('redis')->get('key');
|
||
|
||
The first `get` call for a given key retrieves the value from your cache
|
||
store, but subsequent calls within the same request or job will retrieve the
|
||
value from memory:
|
||
|
||
|
||
|
||
1// Hits the cache...
|
||
|
||
2$value = Cache::memo()->get('key');
|
||
|
||
3
|
||
|
||
4// Does not hit the cache, returns memoized value...
|
||
|
||
5$value = Cache::memo()->get('key');
|
||
|
||
|
||
// Hits the cache...
|
||
$value = Cache::memo()->get('key');
|
||
|
||
// Does not hit the cache, returns memoized value...
|
||
$value = Cache::memo()->get('key');
|
||
|
||
When calling methods that modify cache values (such as `put`, `increment`,
|
||
`remember`, etc.), the memoized cache automatically forgets the memoized value
|
||
and delegates the mutating method call to the underlying cache store:
|
||
|
||
|
||
|
||
1Cache::memo()->put('name', 'Taylor'); // Writes to underlying cache...
|
||
|
||
2Cache::memo()->get('name'); // Hits underlying cache...
|
||
|
||
3Cache::memo()->get('name'); // Memoized, does not hit cache...
|
||
|
||
4
|
||
|
||
5Cache::memo()->put('name', 'Tim'); // Forgets memoized value, writes new value...
|
||
|
||
6Cache::memo()->get('name'); // Hits underlying cache again...
|
||
|
||
|
||
Cache::memo()->put('name', 'Taylor'); // Writes to underlying cache...
|
||
Cache::memo()->get('name'); // Hits underlying cache...
|
||
Cache::memo()->get('name'); // Memoized, does not hit cache...
|
||
|
||
Cache::memo()->put('name', 'Tim'); // Forgets memoized value, writes new value...
|
||
Cache::memo()->get('name'); // Hits underlying cache again...
|
||
|
||
### The Cache Helper
|
||
|
||
In addition to using the `Cache` facade, you may also use the global `cache`
|
||
function to retrieve and store data via the cache. When the `cache` function
|
||
is called with a single, string argument, it will return the value of the
|
||
given key:
|
||
|
||
|
||
|
||
1$value = cache('key');
|
||
|
||
|
||
$value = cache('key');
|
||
|
||
If you provide an array of key / value pairs and an expiration time to the
|
||
function, it will store values in the cache for the specified duration:
|
||
|
||
|
||
|
||
1cache(['key' => 'value'], $seconds);
|
||
|
||
2
|
||
|
||
3cache(['key' => 'value'], now()->addMinutes(10));
|
||
|
||
|
||
cache(['key' => 'value'], $seconds);
|
||
|
||
cache(['key' => 'value'], now()->addMinutes(10));
|
||
|
||
When the `cache` function is called without any arguments, it returns an
|
||
instance of the `Illuminate\Contracts\Cache\Factory` implementation, allowing
|
||
you to call other caching methods:
|
||
|
||
|
||
|
||
1cache()->remember('users', $seconds, function () {
|
||
|
||
2 return DB::table('users')->get();
|
||
|
||
3});
|
||
|
||
|
||
cache()->remember('users', $seconds, function () {
|
||
return DB::table('users')->get();
|
||
});
|
||
|
||
When testing calls to the global `cache` function, you may use the
|
||
`Cache::shouldReceive` method just as if you were [testing the
|
||
facade](/docs/12.x/mocking#mocking-facades).
|
||
|
||
## Atomic Locks
|
||
|
||
To utilize this feature, your application must be using the `memcached`,
|
||
`redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your
|
||
application's default cache driver. In addition, all servers must be
|
||
communicating with the same central cache server.
|
||
|
||
### Managing Locks
|
||
|
||
Atomic locks allow for the manipulation of distributed locks without worrying
|
||
about race conditions. For example, [Laravel Cloud](https://cloud.laravel.com)
|
||
uses atomic locks to ensure that only one remote task is being executed on a
|
||
server at a time. You may create and manage locks using the `Cache::lock`
|
||
method:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Cache;
|
||
|
||
2
|
||
|
||
3$lock = Cache::lock('foo', 10);
|
||
|
||
4
|
||
|
||
5if ($lock->get()) {
|
||
|
||
6 // Lock acquired for 10 seconds...
|
||
|
||
7
|
||
|
||
8 $lock->release();
|
||
|
||
9}
|
||
|
||
|
||
use Illuminate\Support\Facades\Cache;
|
||
|
||
$lock = Cache::lock('foo', 10);
|
||
|
||
if ($lock->get()) {
|
||
// Lock acquired for 10 seconds...
|
||
|
||
$lock->release();
|
||
}
|
||
|
||
The `get` method also accepts a closure. After the closure is executed,
|
||
Laravel will automatically release the lock:
|
||
|
||
|
||
|
||
1Cache::lock('foo', 10)->get(function () {
|
||
|
||
2 // Lock acquired for 10 seconds and automatically released...
|
||
|
||
3});
|
||
|
||
|
||
Cache::lock('foo', 10)->get(function () {
|
||
// Lock acquired for 10 seconds and automatically released...
|
||
});
|
||
|
||
If the lock is not available at the moment you request it, you may instruct
|
||
Laravel to wait for a specified number of seconds. If the lock cannot be
|
||
acquired within the specified time limit, an
|
||
`Illuminate\Contracts\Cache\LockTimeoutException` will be thrown:
|
||
|
||
|
||
|
||
1use Illuminate\Contracts\Cache\LockTimeoutException;
|
||
|
||
2
|
||
|
||
3$lock = Cache::lock('foo', 10);
|
||
|
||
4
|
||
|
||
5try {
|
||
|
||
6 $lock->block(5);
|
||
|
||
7
|
||
|
||
8 // Lock acquired after waiting a maximum of 5 seconds...
|
||
|
||
9} catch (LockTimeoutException $e) {
|
||
|
||
10 // Unable to acquire lock...
|
||
|
||
11} finally {
|
||
|
||
12 $lock->release();
|
||
|
||
13}
|
||
|
||
|
||
use Illuminate\Contracts\Cache\LockTimeoutException;
|
||
|
||
$lock = Cache::lock('foo', 10);
|
||
|
||
try {
|
||
$lock->block(5);
|
||
|
||
// Lock acquired after waiting a maximum of 5 seconds...
|
||
} catch (LockTimeoutException $e) {
|
||
// Unable to acquire lock...
|
||
} finally {
|
||
$lock->release();
|
||
}
|
||
|
||
The example above may be simplified by passing a closure to the `block`
|
||
method. When a closure is passed to this method, Laravel will attempt to
|
||
acquire the lock for the specified number of seconds and will automatically
|
||
release the lock once the closure has been executed:
|
||
|
||
|
||
|
||
1Cache::lock('foo', 10)->block(5, function () {
|
||
|
||
2 // Lock acquired for 10 seconds after waiting a maximum of 5 seconds...
|
||
|
||
3});
|
||
|
||
|
||
Cache::lock('foo', 10)->block(5, function () {
|
||
// Lock acquired for 10 seconds after waiting a maximum of 5 seconds...
|
||
});
|
||
|
||
### Managing Locks Across Processes
|
||
|
||
Sometimes, you may wish to acquire a lock in one process and release it in
|
||
another process. For example, you may acquire a lock during a web request and
|
||
wish to release the lock at the end of a queued job that is triggered by that
|
||
request. In this scenario, you should pass the lock's scoped "owner token" to
|
||
the queued job so that the job can re-instantiate the lock using the given
|
||
token.
|
||
|
||
In the example below, we will dispatch a queued job if a lock is successfully
|
||
acquired. In addition, we will pass the lock's owner token to the queued job
|
||
via the lock's `owner` method:
|
||
|
||
|
||
|
||
1$podcast = Podcast::find($id);
|
||
|
||
2
|
||
|
||
3$lock = Cache::lock('processing', 120);
|
||
|
||
4
|
||
|
||
5if ($lock->get()) {
|
||
|
||
6 ProcessPodcast::dispatch($podcast, $lock->owner());
|
||
|
||
7}
|
||
|
||
|
||
$podcast = Podcast::find($id);
|
||
|
||
$lock = Cache::lock('processing', 120);
|
||
|
||
if ($lock->get()) {
|
||
ProcessPodcast::dispatch($podcast, $lock->owner());
|
||
}
|
||
|
||
Within our application's `ProcessPodcast` job, we can restore and release the
|
||
lock using the owner token:
|
||
|
||
|
||
|
||
1Cache::restoreLock('processing', $this->owner)->release();
|
||
|
||
|
||
Cache::restoreLock('processing', $this->owner)->release();
|
||
|
||
If you would like to release a lock without respecting its current owner, you
|
||
may use the `forceRelease` method:
|
||
|
||
|
||
|
||
1Cache::lock('processing')->forceRelease();
|
||
|
||
|
||
Cache::lock('processing')->forceRelease();
|
||
|
||
## Adding Custom Cache Drivers
|
||
|
||
### Writing the Driver
|
||
|
||
To create our custom cache driver, we first need to implement the
|
||
`Illuminate\Contracts\Cache\Store` [contract](/docs/12.x/contracts). So, a
|
||
MongoDB cache implementation might look something like this:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Extensions;
|
||
|
||
4
|
||
|
||
5use Illuminate\Contracts\Cache\Store;
|
||
|
||
6
|
||
|
||
7class MongoStore implements Store
|
||
|
||
8{
|
||
|
||
9 public function get($key) {}
|
||
|
||
10 public function many(array $keys) {}
|
||
|
||
11 public function put($key, $value, $seconds) {}
|
||
|
||
12 public function putMany(array $values, $seconds) {}
|
||
|
||
13 public function increment($key, $value = 1) {}
|
||
|
||
14 public function decrement($key, $value = 1) {}
|
||
|
||
15 public function forever($key, $value) {}
|
||
|
||
16 public function forget($key) {}
|
||
|
||
17 public function flush() {}
|
||
|
||
18 public function getPrefix() {}
|
||
|
||
19}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Extensions;
|
||
|
||
use Illuminate\Contracts\Cache\Store;
|
||
|
||
class MongoStore implements Store
|
||
{
|
||
public function get($key) {}
|
||
public function many(array $keys) {}
|
||
public function put($key, $value, $seconds) {}
|
||
public function putMany(array $values, $seconds) {}
|
||
public function increment($key, $value = 1) {}
|
||
public function decrement($key, $value = 1) {}
|
||
public function forever($key, $value) {}
|
||
public function forget($key) {}
|
||
public function flush() {}
|
||
public function getPrefix() {}
|
||
}
|
||
|
||
We just need to implement each of these methods using a MongoDB connection.
|
||
For an example of how to implement each of these methods, take a look at the
|
||
`Illuminate\Cache\MemcachedStore` in the [Laravel framework source
|
||
code](https://github.com/laravel/framework). Once our implementation is
|
||
complete, we can finish our custom driver registration by calling the `Cache`
|
||
facade's `extend` method:
|
||
|
||
|
||
|
||
1Cache::extend('mongo', function (Application $app) {
|
||
|
||
2 return Cache::repository(new MongoStore);
|
||
|
||
3});
|
||
|
||
|
||
Cache::extend('mongo', function (Application $app) {
|
||
return Cache::repository(new MongoStore);
|
||
});
|
||
|
||
If you're wondering where to put your custom cache driver code, you could
|
||
create an `Extensions` namespace within your `app` directory. However, keep in
|
||
mind that Laravel does not have a rigid application structure and you are free
|
||
to organize your application according to your preferences.
|
||
|
||
### Registering the Driver
|
||
|
||
To register the custom cache driver with Laravel, we will use the `extend`
|
||
method on the `Cache` facade. Since other service providers may attempt to
|
||
read cached values within their `boot` method, we will register our custom
|
||
driver within a `booting` callback. By using the `booting` callback, we can
|
||
ensure that the custom driver is registered just before the `boot` method is
|
||
called on our application's service providers but after the `register` method
|
||
is called on all of the service providers. We will register our `booting`
|
||
callback within the `register` method of our application's
|
||
`App\Providers\AppServiceProvider` class:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Providers;
|
||
|
||
4
|
||
|
||
5use App\Extensions\MongoStore;
|
||
|
||
6use Illuminate\Contracts\Foundation\Application;
|
||
|
||
7use Illuminate\Support\Facades\Cache;
|
||
|
||
8use Illuminate\Support\ServiceProvider;
|
||
|
||
9
|
||
|
||
10class AppServiceProvider extends ServiceProvider
|
||
|
||
11{
|
||
|
||
12 /**
|
||
|
||
13 * Register any application services.
|
||
|
||
14 */
|
||
|
||
15 public function register(): void
|
||
|
||
16 {
|
||
|
||
17 $this->app->booting(function () {
|
||
|
||
18 Cache::extend('mongo', function (Application $app) {
|
||
|
||
19 return Cache::repository(new MongoStore);
|
||
|
||
20 });
|
||
|
||
21 });
|
||
|
||
22 }
|
||
|
||
23
|
||
|
||
24 /**
|
||
|
||
25 * Bootstrap any application services.
|
||
|
||
26 */
|
||
|
||
27 public function boot(): void
|
||
|
||
28 {
|
||
|
||
29 // ...
|
||
|
||
30 }
|
||
|
||
31}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Providers;
|
||
|
||
use App\Extensions\MongoStore;
|
||
use Illuminate\Contracts\Foundation\Application;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\ServiceProvider;
|
||
|
||
class AppServiceProvider extends ServiceProvider
|
||
{
|
||
/**
|
||
* Register any application services.
|
||
*/
|
||
public function register(): void
|
||
{
|
||
$this->app->booting(function () {
|
||
Cache::extend('mongo', function (Application $app) {
|
||
return Cache::repository(new MongoStore);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
// ...
|
||
}
|
||
}
|
||
|
||
The first argument passed to the `extend` method is the name of the driver.
|
||
This will correspond to your `driver` option in the `config/cache.php`
|
||
configuration file. The second argument is a closure that should return an
|
||
`Illuminate\Cache\Repository` instance. The closure will be passed an `$app`
|
||
instance, which is an instance of the [service
|
||
container](/docs/12.x/container).
|
||
|
||
Once your extension is registered, update the `CACHE_STORE` environment
|
||
variable or `default` option within your application's `config/cache.php`
|
||
configuration file to the name of your extension.
|
||
|
||
## Events
|
||
|
||
To execute code on every cache operation, you may listen for various
|
||
[events](/docs/12.x/events) dispatched by the cache:
|
||
|
||
Event Name
|
||
---
|
||
`Illuminate\Cache\Events\CacheFlushed`
|
||
`Illuminate\Cache\Events\CacheFlushing`
|
||
`Illuminate\Cache\Events\CacheHit`
|
||
`Illuminate\Cache\Events\CacheMissed`
|
||
`Illuminate\Cache\Events\ForgettingKey`
|
||
`Illuminate\Cache\Events\KeyForgetFailed`
|
||
`Illuminate\Cache\Events\KeyForgotten`
|
||
`Illuminate\Cache\Events\KeyWriteFailed`
|
||
`Illuminate\Cache\Events\KeyWritten`
|
||
`Illuminate\Cache\Events\RetrievingKey`
|
||
`Illuminate\Cache\Events\RetrievingManyKeys`
|
||
`Illuminate\Cache\Events\WritingKey`
|
||
`Illuminate\Cache\Events\WritingManyKeys`
|
||
|
||
To increase performance, you may disable cache events by setting the `events`
|
||
configuration option to `false` for a given cache store in your application's
|
||
`config/cache.php` configuration file:
|
||
|
||
|
||
|
||
1'database' => [
|
||
|
||
2 'driver' => 'database',
|
||
|
||
3 // ...
|
||
|
||
4 'events' => false,
|
||
|
||
5],
|
||
|
||
|
||
'database' => [
|
||
'driver' => 'database',
|
||
// ...
|
||
'events' => false,
|
||
],
|
||
|