Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Add lock support for file and null cache drivers #35139

Merged
merged 5 commits into from Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/Illuminate/Cache/CacheLock.php
@@ -0,0 +1,85 @@
<?php

namespace Illuminate\Cache;

class CacheLock extends Lock
{
/**
* The cache store implementation.
*
* @var \Illuminate\Contracts\Cache\Store
*/
protected $store;

/**
* Create a new lock instance.
*
* @var \Illuminate\Contracts\Cache\Store
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return void
*/
public function __construct($store, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds, $owner);

$this->store = $store;
}

/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
if (method_exists($this->store, 'add') && $this->seconds > 0) {
return $this->store->add(
$this->name, $this->owner, $this->seconds
);
}

if (! is_null($this->store->get($this->name))) {
return false;
}

return ($this->seconds > 0)
? $this->store->put($this->name, $this->owner, $this->seconds)
: $this->store->forever($this->name, $this->owner, $this->seconds);
}

/**
* Release the lock.
*
* @return bool
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
return $this->store->forget($this->name);
}

return false;
}

/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
$this->store->forget($this->name);
}

/**
* Returns the owner value written into the driver for this lock.
*
* @return mixed
*/
protected function getCurrentOwner()
{
return $this->store->get($this->name);
}
}
45 changes: 43 additions & 2 deletions src/Illuminate/Cache/FileStore.php
Expand Up @@ -3,13 +3,15 @@
namespace Illuminate\Cache;

use Exception;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Filesystem\LockTimeoutException;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\InteractsWithTime;

class FileStore implements Store
class FileStore implements Store, LockProvider
{
use InteractsWithTime, RetrievesMultipleKeys;
use InteractsWithTime, RetrievesMultipleKeys, HasCacheLock;

/**
* The Illuminate Filesystem instance.
Expand Down Expand Up @@ -83,6 +85,45 @@ public function put($key, $value, $seconds)
return false;
}

/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function add($key, $value, $seconds)
{
$this->ensureCacheDirectoryExists($path = $this->path($key));

$file = $this->files->newFileForReadWrite($path);

try {
$file->getExclusiveLock();
} catch (LockTimeoutException $e) {
$file->close();

return false;
}

$expire = $file->read(10);

if (empty($expire) || $this->currentTime() >= $expire) {
$file->truncate()
->write($this->expiration($seconds).serialize($value))
->close();

$this->ensureFileHasCorrectPermissions($path);

return true;
}

$file->close();

return false;
}

/**
* Create the file cache directory if necessary.
*
Expand Down
31 changes: 31 additions & 0 deletions src/Illuminate/Cache/HasCacheLock.php
@@ -0,0 +1,31 @@
<?php

namespace Illuminate\Cache;

trait HasCacheLock
{
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new CacheLock($this, $name, $seconds, $owner);
}

/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
}
46 changes: 46 additions & 0 deletions src/Illuminate/Cache/NoLock.php
@@ -0,0 +1,46 @@
<?php

namespace Illuminate\Cache;

class NoLock extends Lock
{
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
return true;
}

/**
* Release the lock.
*
* @return bool
*/
public function release()
{
return true;
}

/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
//
}

/**
* Returns the owner value written into the driver for this lock.
*
* @return mixed
*/
protected function getCurrentOwner()
{
return $this->owner;
}
}
29 changes: 28 additions & 1 deletion src/Illuminate/Cache/NullStore.php
Expand Up @@ -2,7 +2,9 @@

namespace Illuminate\Cache;

class NullStore extends TaggableStore
use Illuminate\Contracts\Cache\LockProvider;

class NullStore extends TaggableStore implements LockProvider
{
use RetrievesMultipleKeys;

Expand Down Expand Up @@ -96,4 +98,29 @@ public function getPrefix()
{
return '';
}

/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new NoLock($name, $seconds, $owner);
}

/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
}
10 changes: 10 additions & 0 deletions src/Illuminate/Contracts/Filesystem/LockTimeoutException.php
@@ -0,0 +1,10 @@
<?php

namespace Illuminate\Contracts\Filesystem;

use Exception;

class LockTimeoutException extends Exception
{
//
}