Reaching the limits of Cron
If you’re reading this blog post on account of the title, hopefully you already have a good understanding of what Cron is and you may well also be aware of some of it’s limitations. There are some situations when Cron use simply becomes impractical; the Cron minimum timeout of 1 minute can be prohibitively long to wait, rendering Cron an unhelpful tool for that particular project or situation.
In such moments I use Linux Upstart. It can run your code without a timeout at all or you can choose to set the timeout that you need.
Setting up Upstart
It’s very simple to set this up. To make a Symfony command I use daemonizable-command and write something like this:
namespace …\Command; | |
use Wrep\Daemonizable\Command\EndlessContainerAwareCommand; | |
use Symfony\Component\Console\Output\OutputInterface; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | |
use Symfony\Component\Console\Input\InputArgument; | |
use Symfony\Component\Console\Input\InputOption; | |
class UpdateCommand extends EndlessContainerAwareCommand { | |
protected function configure() { | |
parent::configure(); | |
$this | |
->setName(‘<some name>') | |
->setDescription(‘<some description>') | |
->setHelp( "Use -q to suppress all output \n | |
Use --run-once to only run the command once, usefull for debugging \n | |
Use --detect-leaks to print a memory usage report after each run, read more in the next section" ) | |
->setTimeout(0.5); //set timeout what your need | |
} | |
protected function execute(InputInterface $input, OutputInterface $output) | |
{ | |
// here your code, if you need more timeout in some cases use “sleep(<time>)” | |
} | |
} |
Configuration
After that you’ll need to add an Upstart configuration file in Ubuntu.
The file extension should be “conf” eg,“project-task.conf”. Put the file into /etc/init/ or $HOME/.init/
description “<some description>" | |
author “<autor name, email etc.>” | |
start on (local-filesystems and net-device-up) | |
stop on shutdown | |
console log | |
respawn | |
respawn limit 5 60 | |
exec project-url/app/console <commend name> -e prod —no-debug |
You can find more configurations here.
Control
Now you can control your command like a Ubuntu service:
service <project-task> status/stop/start/restart
Bonus
And for more usability I’ve written a small task for checking the status of and restarting these Upstart tasks. This one’s run by cron 🙂
class DaemonCheckCommand extends ContainerAwareCommand { | |
protected function configure() { | |
parent::configure(); | |
$this | |
->setName('daemon-check') | |
->setDescription('Check daemons if it crased restarted it'); | |
} | |
protected function execute(InputInterface $input, OutputInterface $output) | |
{ | |
$status= false; | |
$check = `echo ‘<password for this sudo user >' | sudo -S status <task name>`; | |
if ( strpos($check, 'start/running') === false ) { | |
$startingCheck = `echo '<password for this sudo user >' | sudo -S start <task name>`; | |
if ( strpos($startingCheck, 'start/running') === false ) { | |
$status .= "fail to start <task name> \n"; | |
} else { | |
$status .= "restarted <task name> \n"; | |
} | |
} | |
if ( $status ) { | |
$to = array( | |
'email' => ‘name', | |
….. | |
‘email-n' => ‘name-n' | |
); | |
$message = \Swift_Message::newInstance() | |
->setSubject('Project daemon crashed') | |
->setFrom(array('support@project.com' => 'Project')) | |
->setTo($to) | |
->setContentType("text/html") | |
->setBody(date('Y-m-d H:i') ."\n". $status); | |
$this->getContainer()->get('mailer')->send($message); | |
} | |
$this->getContainer()->get( 'logger' )->err('---Daemon Check = ' . ($status ? $status : (date('Y-m-d H:i') . ' working')) ); | |
return $status; | |
} | |
} |
I hope this makes your life easier 😉