More granular cronjobs in openshift using PHP
The other day came across the nice task of moving a whole site and CMS to openshift. In case you don’t know what I’m talking about: Openshift is PaAS from RedHat that in contrast to other PaAS providers doesn’t use linux containers (like Heroku)
I like openshift over other providers because the free quota they provide is quite enough to start with small sites.
In the other hand, when you choose server your application from a PaAS instead a VPS/dedicated server, etc. you also should know there are hard limits.
While you can get cronjobs, the current support of the addon only let you schedule tasks to run minutely / hourly / daily / weekly / monthly but wont allow you to run cron expressions and have something like */3 * * * *
You could get the current date and see if the divider matches your magic number…
Or you can come with something better…
Looking at packagist I found a nice project that will do all the dirty job for us: mtdowling/cron-expression is a component that parses cron expressions and let us match a date-time against such expression.
In order to make the script generic enough I also used ulrichsg/getopt-php but of course you could avoid this…
So cron-expression
will take the parameters sent to the scripts using getopt-php
, then parse the cron expression and run the command whenever the date matches the expression.
This is how my composer.json
looked like
{
"name": "site",
"require": {
"mtdowling/cron-expression": "1.0.*",
"ulrichsg/getopt-php": "2.1.*"
}
}
then I created an script (I called it) cron.php
(original, ha!)
<?php
set_time_limit(0);
require __DIR__ . '/vendor/autoload.php';
$commandOpt = new Ulrichsg\Getopt\Option('c', 'command',
Ulrichsg\Getopt\Getopt::REQUIRED_ARGUMENT);
$commandOpt->setDescription("the command to run");
$scheduleOpt = new Ulrichsg\Getopt\Option('s', 'schedule',
Ulrichsg\Getopt\Getopt::REQUIRED_ARGUMENT);
$scheduleOpt->setDescription("the cron schedule to follow");
$getopt = new Ulrichsg\Getopt\Getopt(array(
$commandOpt,
$scheduleOpt));
try {
$getopt->parse();
} catch (Exception $e) {
echo $getopt->getHelpText();
exit(1);
}
$command = $getopt->getOption('command');
$schedule = $getopt->getOption('schedule');
$cron = \Cron\CronExpression::factory($schedule);
if ($cron->isDue()) {
passthru($command);
}
There are few things that are pretty obvious…for example I added passthru
in order to run the command so the command’s output is sent back to the caller. I also removed some other stuff to make the example shorter.
The next step was to setup each task with their required schedule, I did so by creating a file into .openshift/minutely
something like
#!/bin/bash
env php /path/to/cron.php --schedule="*/3 * * * *" --command="php run_every_3_minutes.php"
env php /path/to/cron.php --schedule="*/15 4 * * *" --command="php another_script.php" // this will run every 15 minutes at 4am
Also, be aware that openshift imposes a 20 minutes limit for cronjobs and then scripts will be killed. In that case you should consider de-attaching the long-running process using nohup
That’s it…you now have a way to have more granular cronjobs in openshift using PHP