naowas/console-mutex

Mutex for Laravel Console Commands.


README

Mutex for Laravel Console Commands

Laravel Console Mutex

Buy me a coffee

StyleCI Build Status Coverage Status

Packagist Version Packagist Stars Packagist Downloads Packagist License

Mutex for Laravel Console Commands.

Laravel Console Mutex - Demo

Table of contents

Usage

  1. Install the package via Composer:

    composer require illuminated/console-mutex
  2. Use Illuminated\Console\WithoutOverlapping trait:

    use Illuminated\Console\WithoutOverlapping;
    
    class ExampleCommand extends Command
    {
        use WithoutOverlapping;
    
        // ...
    }

Strategies

Mutex can prevent overlapping by using various strategies:

  • file (default)
  • mysql
  • redis
  • memcached

The default file strategy is acceptable for small applications, which are deployed on a single server. If your application is more complex and deployed on several nodes, you should consider using another mutex strategy.

You can change strategy by using the $mutexStrategy field:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    protected string $mutexStrategy = 'mysql';

    // ...
}

Or by using the setMutexStrategy() method:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    public function __construct()
    {
        parent::__construct();

        $this->setMutexStrategy('mysql');
    }

    // ...
}

Advanced

Set custom timeout

By default, if mutex sees that the command is already running, it will immediately quit. You can change that behavior by setting a timeout in which mutex can wait for another running command to finish its execution.

You can set the timeout by specifying the $mutexTimeout field:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    // In milliseconds
    protected ?int $mutexTimeout = 3000;

    // ...
}

Or by using the setMutexTimeout() method:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    public function __construct()
    {
        parent::__construct();

        // In milliseconds
        $this->setMutexTimeout(3000);
    }

    // ...
}

Here's how the $mutexTimeout field is treated:

  • 0 - no waiting (default);
  • {int} - wait for the given number of milliseconds;
  • null - wait for the running command to finish its execution;

Handle multiple commands

Sometimes it might be useful to have a shared mutex for multiple commands. You can easily achieve that by setting the same mutex name for all of those commands.

You should use the getMutexName() method for that:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    public function getMutexName()
    {
        return 'shared-for-command1-and-command2';
    }

    // ...
}

Set custom storage folder

If you're using the file strategy, mutex files would be stored in the storage/app folder.

You can change that by overriding the getMutexFileStorage() method:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    public function getMutexFileStorage()
    {
        return storage_path('my/custom/path');
    }

    // ...
}

Troubleshooting

Trait included, but nothing happens?

WithoutOverlapping trait overrides the initialize() method:

trait WithoutOverlapping
{
    protected function initialize(InputInterface $input, OutputInterface $output)
    {
        $this->initializeMutex();

        parent::initialize($input, $output);
    }

    // ...
}

If your command overrides the initialize() method too, you have to call the initializeMutex() method by yourself:

class ExampleCommand extends Command
{
    use WithoutOverlapping;

    protected function initialize(InputInterface $input, OutputInterface $output)
    {
        // You have to call it first
        $this->initializeMutex();

        // Then goes your custom code
        $this->foo = $this->argument('foo');
        $this->bar = $this->argument('bar');
        $this->baz = $this->argument('baz');
    }

    // ...
}

Several traits conflict?

If you're using another illuminated/console-% package, you'll get the "traits conflict" error.

For example, if you're building a loggable command, which doesn't allow overlapping:

class ExampleCommand extends Command
{
    use Loggable;
    use WithoutOverlapping;

    // ...
}

You'll get the traits conflict, because both of those traits are overriding the initialize() method:

If two traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved.

To fix that - override the initialize() method and resolve the conflict:

class ExampleCommand extends Command
{
    use Loggable;
    use WithoutOverlapping;

    protected function initialize(InputInterface $input, OutputInterface $output)
    {
        // Initialize conflicting traits
        $this->initializeMutex();
        $this->initializeLogging();
    }

    // ...
}

Sponsors

Laravel Idea
Material Theme UI Plugin

License

Laravel Console Mutex is open-sourced software licensed under the MIT license.

Support on Patreon  Buy me a coffee