master

laravel/framework

Last updated at: 29/12/2023 09:20

CacheCommandMutex.php

TLDR

This file contains the implementation of the CacheCommandMutex class in the Illuminate\Console namespace. This class is responsible for creating, checking the existence, and releasing mutexes for console commands using a cache store.

Methods

create

This method attempts to obtain a command mutex for the given command. It uses the cache store specified in the class to create a lock or add a cache entry with a true value and expiration time. If the cache store supports locks, it uses the lock() method to create a lock and returns the result. Otherwise, it adds a cache entry using the add() method and returns the result.

exists

This method determines if a command mutex exists for the given command. It uses the cache store specified in the class to check if a cache entry with the command mutex name exists. If the cache store supports locks, it creates a lock and checks if it exists. If it exists, it releases the lock before returning the opposite result.

forget

This method releases the mutex for the given command. It uses the cache store specified in the class to either release the lock or forget the cache entry with the command mutex name. If the cache store supports locks, it uses the forceRelease() method to release the lock and returns the result. Otherwise, it uses the forget() method and returns the result.

commandMutexName

This method returns the isolatable command mutex name for the given command. It constructs the base name with the format 'framework/command-' + command name. If the command has a method isolatableId(), it appends '-'+ command->isolatableId() to the base name. Finally, it returns the constructed name.

useStore

This method specifies the cache store that should be used. It sets the store property of the class with the given store parameter and returns $this.

shouldUseLocks

This method determines if the given cache store should use locks for command mutexes. It checks if the store is an instance of LockProvider and not an instance of DynamoDbStore. It returns the result.

<?php

namespace Illuminate\Console;

use Carbon\CarbonInterval;
use Illuminate\Cache\DynamoDbStore;
use Illuminate\Contracts\Cache\Factory as Cache;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Support\InteractsWithTime;

class CacheCommandMutex implements CommandMutex
{
    use InteractsWithTime;

    /**
     * The cache factory implementation.
     *
     * @var \Illuminate\Contracts\Cache\Factory
     */
    public $cache;

    /**
     * The cache store that should be used.
     *
     * @var string|null
     */
    public $store = null;

    /**
     * Create a new command mutex.
     *
     * @param  \Illuminate\Contracts\Cache\Factory  $cache
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    /**
     * Attempt to obtain a command mutex for the given command.
     *
     * @param  \Illuminate\Console\Command  $command
     * @return bool
     */
    public function create($command)
    {
        $store = $this->cache->store($this->store);

        $expiresAt = method_exists($command, 'isolationLockExpiresAt')
            ? $command->isolationLockExpiresAt()
            : CarbonInterval::hour();

        if ($this->shouldUseLocks($store->getStore())) {
            return $store->getStore()->lock(
                $this->commandMutexName($command),
                $this->secondsUntil($expiresAt)
            )->get();
        }

        return $store->add($this->commandMutexName($command), true, $expiresAt);
    }

    /**
     * Determine if a command mutex exists for the given command.
     *
     * @param  \Illuminate\Console\Command  $command
     * @return bool
     */
    public function exists($command)
    {
        $store = $this->cache->store($this->store);

        if ($this->shouldUseLocks($store->getStore())) {
            $lock = $store->getStore()->lock($this->commandMutexName($command));

            return tap(! $lock->get(), function ($exists) use ($lock) {
                if ($exists) {
                    $lock->release();
                }
            });
        }

        return $this->cache->store($this->store)->has($this->commandMutexName($command));
    }

    /**
     * Release the mutex for the given command.
     *
     * @param  \Illuminate\Console\Command  $command
     * @return bool
     */
    public function forget($command)
    {
        $store = $this->cache->store($this->store);

        if ($this->shouldUseLocks($store->getStore())) {
            return $store->getStore()->lock($this->commandMutexName($command))->forceRelease();
        }

        return $this->cache->store($this->store)->forget($this->commandMutexName($command));
    }

    /**
     * Get the isolatable command mutex name.
     *
     * @param  \Illuminate\Console\Command  $command
     * @return string
     */
    protected function commandMutexName($command)
    {
        $baseName = 'framework'.DIRECTORY_SEPARATOR.'command-'.$command->getName();

        return method_exists($command, 'isolatableId')
            ? $baseName.'-'.$command->isolatableId()
            : $baseName;
    }

    /**
     * Specify the cache store that should be used.
     *
     * @param  string|null  $store
     * @return $this
     */
    public function useStore($store)
    {
        $this->store = $store;

        return $this;
    }

    /**
     * Determine if the given store should use locks for command mutexes.
     *
     * @param  \Illuminate\Contracts\Cache\Store  $store
     * @return bool
     */
    protected function shouldUseLocks($store)
    {
        return $store instanceof LockProvider && ! $store instanceof DynamoDbStore;
    }
}