3

I want the cron run only once at say 01:00 hours after midnight, just to save shared hosting resources as cron empties the caches. My new site is using too much of resources and I'm trying my best to stick with shared hosting. I want to set cron run once in a day. It should run at midnight so that some cache is built before day time, when traffic is high.

I've only two somewhat related threads: make sure hook cron is only run once a day setting and How to run cron more often than every hour?

But for such simple thing it don't want to use Elysia module.

From this post: How do I get the (date)timestamp since last cron run? I understand if we manually set cron_last variable then may be next cron time will be calculated with respect to this.

Another way I can do is to run drush cron from a script from crontab which will run it at a particular time. Similarly we can call cron from crontab ( as in here).

I'd want it to be done from within a module etc so that whenever I move my site, it'll work at any new setup without doing any configuration.

Any suggestions?

4
  • you can run some jobs every day at a specified hour using drupal.org/project/elysia_cron
    – Bala
    May 20, 2013 at 17:30
  • @Bala "[...]don't want to use Elysia module."
    – Chapabu
    May 20, 2013 at 17:32
  • Why can't you just set your crontab up to hit cron.php at the specified time?
    – Chapabu
    May 20, 2013 at 17:33
  • I'm having dozen sites, and keep moving between hosting accounts etc. This will increase maintenance steps which I'd need to remember.
    – AgA
    May 20, 2013 at 17:39

2 Answers 2

4

I think you've covered the options well in your question. However, your combinaton of requirements - i.e.:

  1. no poking of cron.php from crontab;
  2. settings kept in database to be migrated along with the site ("done from within a module");
  3. the management module must not be Elysia.

is a problem. This combination excludes all the possible answers. There is no solution that will satisfy all three requirements.

If you want to use a module, that module is Elysia cron.

You seem to think Elysia cron is overkill for this. I happen to agree. Besides, Elysia does not work too well when triggered by the built-in default poorman's cron. You really want to use an external crontab poke together with Elysia to get the type of fine-grained control you seem to be after, with Elysia.

So while your first requirement rules out poking of cron.php from crontab, that is my suggested solution.

Actually, since the crontab entries for all your sites can be kept in a single file on a single server, I do not see how this set-up should be considered too difficult to maintain.

1
  • suppose I've set cron to be run once in a day and then in my hook_cron module if I set cron_last with a calculated time so that it runs next day as per cron_last set by me -- will it not work?
    – AgA
    May 21, 2013 at 5:05
3

This is an old question, but I recently had to implement a similar feature in Drupal 8, so this might be helpful to others.

Guarantee that a cron task runs exactly once per day, as close to a specified time of day as possible, without any control over when the cron itself runs.

The basic idea is to keep the time of the last execution in a state variable, then check whether the scheduled time of day has passed by between then and now. For example, if the scheduled time is at 23:30, the last run was Jan 1 00:06, and now it is Jan 1 23:29, it shouldn't run - but if it is Jan 1 23:40, it should.

This ensures that the runs will be (on average) 24 hours apart, and the task happens on the first cron execution after the scheduled time. (The more frequently Drupal cron is run, the closer it will be.)

Rough structure of the service:

class MyModuleCron {

  public function cron() {
    $now = \Drupal::time()->getRequestTime();
    if ($this->shouldRun($now)) {
      $this->queueTasks();
      $this->state->set('mymodule.last_cron', $now);
    }
  }

  public function shouldRun($now) {
    // Get these from config or something:
    $scheduled = '07:30';
    $timezone = new \DateTimeZone('Region/City');

    $timestamp_last = $this->state->get('mymodule.last_cron') ?? 0;
    $last = \DateTime::createFromFormat('U', $timestamp_last)
      ->setTimezone($timezone);
    $next = clone $last;

    $next->setTime(...explode(':', $scheduled));
    // If the cron ran on the same calendar day it should have, add one day.
    if ($next->getTimestamp() <= $last->getTimestamp()) {
      $next->modify('+1 day');
    }

    return $next->getTimestamp() <= $now;
  }

  public function queueTasks() { }
}

And the hook_cron:

function mymodule_cron() {
  \Drupal::service('mymodule.cron')->cron();
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.