Last active
October 18, 2018 04:14
-
-
Save m8rge/3bdee6d500265fc26bb2 to your computer and use it in GitHub Desktop.
Yii2 console controller behavior. Prevents double run console command
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace console\components; | |
use Throwable; | |
use yii\base\Action; | |
class CronException extends \RuntimeException | |
{ | |
/** | |
* @var string | |
*/ | |
public $actionId; | |
/** | |
* @var string | |
*/ | |
public $since; | |
public function __construct( | |
string $failType, | |
Action $action, | |
string $lockFileName, | |
int $code = 0, | |
Throwable $previous = null | |
) | |
{ | |
$this->actionId = $action->uniqueId; | |
if (file_exists($lockFileName)) { | |
$this->since = date('r', filemtime($lockFileName)); | |
} | |
parent::__construct($this->actionId . ' ' . $failType, $code, $previous); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace console\components; | |
use Yii; | |
use yii\base\ActionEvent; | |
use yii\base\Behavior; | |
use yii\base\Exception; | |
use yii\console\Controller; | |
/** | |
* yii\console\Controller behavior | |
* Prevents double run console command | |
*/ | |
class OneInstance extends Behavior | |
{ | |
/** | |
* @var string | |
*/ | |
protected $lockFileName; | |
/** | |
* If process executes more than $longRunningTimeout seconds, | |
* it will be considered as long-running | |
* and warning message will be logged | |
* @var int seconds | |
*/ | |
public $longRunningTimeout = 3600; // 1h | |
/** | |
* Start command if there isn't another active instance. Even last run was unsuccessfully completed | |
* @var bool | |
*/ | |
public $nonFailureShutdown = false; | |
public function events() | |
{ | |
return [ | |
Controller::EVENT_BEFORE_ACTION => 'checkInstance', | |
Controller::EVENT_AFTER_ACTION => 'cleanUp', | |
]; | |
} | |
/** | |
* @param ActionEvent $event | |
* @throws Exception | |
*/ | |
public function checkInstance(ActionEvent $event): void | |
{ | |
$this->lockFileName = Yii::$app->runtimePath . '/' . str_replace('/', '-', $event->action->uniqueId) . '.lock'; | |
if (file_exists($this->lockFileName)) { | |
$processExists = file_exists('/proc/' . trim(file_get_contents($this->lockFileName))); | |
if ($processExists) { | |
$event->isValid = false; | |
if (filemtime($this->lockFileName) + $this->longRunningTimeout < time()) { | |
throw new CronException('fall asleep', $event->action, $this->lockFileName); | |
} | |
return; | |
} | |
if ($this->nonFailureShutdown) { | |
@unlink($this->lockFileName); | |
} else { | |
$event->isValid = false; | |
throw new CronException('failed', $event->action, $this->lockFileName); | |
} | |
} | |
if (!file_put_contents($this->lockFileName, posix_getpid())) { | |
throw new CronException('Can\'t write lock file', $event->action, $this->lockFileName); | |
} | |
} | |
/** | |
* @param ActionEvent $event | |
*/ | |
public function cleanUp($event) | |
{ | |
if ($event->result == 0) { | |
@unlink($this->lockFileName); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment