jdavidbakr / MultiServerEvent

Laravel plugin to allow scheduled events across multiple servers with the same scheduler to not overlap.
MIT License
37 stars 17 forks source link

Multiserver still runs multiple crons #15

Closed webmake closed 7 years ago

webmake commented 7 years ago

POC: create 2commands, first should log, second log after sleep for a while

namespace App\Console;

use Illuminate\Console\Command;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use jdavidbakr\MultiServerEvent\Scheduling\Schedule as ModuleSchedule;

class Kernel extends ConsoleKernel
{
    protected $commands = [
        \jdavidbakr\MultiServerEvent\Commands\MultiServerMigrationService::class,
        Command1::class,
        Command2::class,
    ];

    protected function schedule(Schedule $schedule)
    {
        $schedule->command(Command1::class)
            ->everyMinute()
            ->withoutOverlappingMultiServer();
        $schedule->command(Command2::class)
            ->everyMinute()
            ->withoutOverlappingMultiServer();
    }

    protected function commands()
    {
        require base_path('routes/console.php');
    }

    protected function defineConsoleSchedule()
    {
        $this->app->instance(
            Schedule::class,
            $schedule = new ModuleSchedule()
        );

        $this->schedule($schedule);
    }
}

class Command1 extends Command
{
    protected $signature = 'command:first';

    public function handle()
    {
        logger($this->signature);
    }
}

class Command2 extends Command
{
    protected $signature = 'command:second';

    public function handle()
    {
        sleep(1);
        logger($this->signature);
    }
}

Running with: php artisan schedule:run & more than once, you can see:

$ Running scheduled command: '/usr/bin/php7.1' 'artisan' command:first > '/dev/null' 2>&1
Running scheduled command: '/usr/bin/php7.1' 'artisan' command:second > '/dev/null' 2>&1
No scheduled commands are ready to run.
No scheduled commands are ready to run.

This is very good and expected, but when increment sleep for example till 11 seconds, first command will be executed again, due hardcoded 10 seconds

Since 5.4.26 laravel released very usefull feature, and can be reused https://github.com/laravel/framework/pull/19537/files to solve issue, when saving next execution time, and when second server will run same crons, it could check is it right time to execute already ran crons.

Because clean up older than 10 seconds, not solve fully problem. It is only visible having multiple crons, with various duration, and it is normal explanation, but this POC proves insufficient check

webmake commented 7 years ago

image Some visuals to explain situation, and according sample:

namespace App\Console;

use Illuminate\Console\Command;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use jdavidbakr\MultiServerEvent\Scheduling\Schedule as ModuleSchedule;

class Kernel extends ConsoleKernel
{
    protected $commands = [
        \jdavidbakr\MultiServerEvent\Commands\MultiServerMigrationService::class,
        Command1::class,
        Command2::class,
        Command3::class,
    ];

    protected function schedule(Schedule $schedule)
    {
        $schedule->command(Command1::class)
            ->everyMinute()
            ->withoutOverlappingMultiServer();
        $schedule->command(Command2::class)
            ->everyMinute()
            ->withoutOverlappingMultiServer();
        $schedule->command(Command3::class)
            ->everyMinute()
            ->withoutOverlappingMultiServer();
    }

    protected function commands()
    {
        require base_path('routes/console.php');
    }

    protected function defineConsoleSchedule()
    {
        $this->app->instance(
            Schedule::class,
            $schedule = new ModuleSchedule()
        );

        $this->schedule($schedule);
    }
}

class Command1 extends Command
{
    protected $signature = 'command:first';

    public function handle()
    {
        sleep(3);
        logger($this->signature);
    }
}

class Command2 extends Command
{
    protected $signature = 'command:second';

    public function handle()
    {
        sleep(20);
        logger($this->signature);
    }
}
class Command3 extends Command
{
    protected $signature = 'command:third';

    public function handle()
    {
        sleep(4);
        logger($this->signature);
    }
}

crontab (which simulates 2servers)

* * * * * php /project/artisan schedule:run
* * * * * php /project/artisan schedule:run

log output

[2017-06-13 18:04:09] local.DEBUG: command:first  
[2017-06-13 18:04:16] local.DEBUG: command:third  
[2017-06-13 18:04:27] local.DEBUG: command:second  
[2017-06-13 18:04:33] local.DEBUG: command:third