Hi,

I detect some bug that appear in the rule when you set */45 * * * * job

The job is running at ex: 15:45 then 16:00 then 16:45.

Must be 16:30 then 17:05 then 17:50 ... etc.

I guess this lines

<?php
$nowTDelta = $nowT - $lastT + ($lastT > $nowT ? 12 * 31 * 24 * 60 : 0);

$nextTDelta = $nextT - $lastT + ($lastT > $nextT ? 12 * 31 * 24 * 60 : 0);
?>

I'm trying to figureout this. I'll keeping in touch.

Comments

killua99’s picture

I figureout what is the cause. The internal function _cronDecodeRule(),

the code is

<?php
// Used by elysia_cron_should_run
function _cronDecodeRule($rule, $min, $max) {
  if ($rule == '*')
    return range($min, $max);

  $result = array();
  foreach (explode(',', $rule) as $token) {
    if (preg_match('/^([0-9]+)-([0-9]+)$/', $token, $r)) {
      $result = array_merge($result, range($r[1], $r[2]));
    }
    elseif (preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
      for ($i = $min; $i <= $max; $i++)
        if ($i % $r[1] == 0)
          $result[] = $i;
    }
    elseif (is_numeric($token)) {
      $result[] = $token;
    }
  }
  return $result;
}

// thats means if I put this rule

$rule = _cronDecodeRule('*/45', 0, 59); // to determine mins between every cron this will be the output

print_r($rule);
// array(0 => 0, 1 => 45);

/**
 * So with this pice of code how you can determine when execute a job every 45 min. 
 *
 *    elseif (preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
 *      for ($i = $min; $i <= $max; $i++)
 *        if ($i % $r[1] == 0)
 *          $result[] = $i;
 *    }
 *
 * I guess this is a wrong way to determine the exactly next job.
 */

?>

The fast way I can think on how to fix this is to see if I'm decoding mins, so the code will be like this.

<?php
// Used by elysia_cron_should_run
function _cronDecodeRule($rule, $min, $max, $lastRun = 0) {
  if ($rule == '*')
    return range($min, $max);

  $result = array();
  foreach (explode(',', $rule) as $token) {
    if (preg_match('/^([0-9]+)-([0-9]+)$/', $token, $r)) {
      $result = array_merge($result, range($r[1], $r[2]));
    }
    // For determine minutes
    elseif ($lastRun > 0 && preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
      $result[] = date('i', strtotime('+' . $r[1] . ' minutes', $lastRun));
    }
    // For determine month, days, hours, weeks, days of weeks,
    elseif ($lastRun ==  0 && preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
      for ($i = $min; $i <= $max; $i++)
        if ($i % $r[1] == 0)
          $result[] = $i;
    }
    elseif (is_numeric($token)) {
      $result[] = $token;
    }
  }
  return $result;
}
?>

Hope to get some answers.

niteman’s picture

Title: bug in every 45 min » bug in every X minutes rules when remainder of 60 divided by X <> 0

Sorry killua99, but I don't like your approach.

I think it should be better to create a _cronDecodeMinutesRule function, since we can determine if we are dealing with minutes prior to invoke the function.

Just a suggestion from a non-developer.

Best regards

PS: Just realized this affects also hours and days and we always need to know last execution time

killua99’s picture

@NITEMAN: I don't like that quickly fix I did, thats why I said "The fast way I can think on how to fix ..." I believe that we don't need to create another function just for one small job (calculate de time in the crondecode) when you can put in a simple line like:

<?php
// in the same function elysia_cron_should_run().
preg_match('/^\*\/([0-9]+)$/', $token, $r);
$rules[1] = date('i', strtotime('+' . $r[1] . ' minutes', $lastRun));
?>

So this is not a patch to solve this bug, is just a bit of code to figure out how to solve this issue. I see it is not using the last_run timestamp to make a real aproach to the next job.

For sure, it needed (my point of view) to be remaking this part of the code, to handled in a good way this important step.

PS: Thanks for change the title of this bug, is more specific.

niteman’s picture

@killua99, excuse me just after sending my comment I realized rule last run (or launch) time is needed always in order to determine if a */x rule is to be run when x is not a divider of the max value.

That's applicable for minutes, hours, days of month, months of year and days of week.

So a general fix (or at least a warning about it) is needed.

My php knowledge is not enough to write a fix... but I think we should take a look to linux code which perform the same task I don't think it is so trivial to fix this.

Another place to look at maybe this: http://d9t.de/open-source/php/cron.php5/cron.php5-sourcecode

I'm sorry currently I don't have the time to help more with this :(

If you want we can have a chat about this issue in Madrid after September 18 or arrange a code sprint on Seville at DrupalCamp Spain 2011.

Best regards.

PS: Tittle is not accurate since reflects only the minutes issue, but I don't imagine a more descriptive one.

gotheric’s picture

I know that cronDecodeRule has some problems...

It's a complicated part, and at the time i coded it (when this module was for me only...) it was a quick way to solve 90% of real uses cases (and 100% of my needs).
(I think "*/45" rule is not used very often in real use case, it involves a "weird" clock pattern....)

However, even if they are minor problems, they should be fixed.

I think the best way is to look at the code that linux crontab uses, and replicates it in PHP (but it's not very easy to dig in it...)
Or find another free PHP lib that handles the clock better to extract its method (but i searched for it years ago and i found nothing...)

But for the next weeks i've few time to spend in this task....

If someone want to propose a better cron decode rule, post it here!
I've a lot of unit tests to "stress" it and see if it works!

gotheric’s picture

@NITEMAN: Thanks for the link to that library, i'll try to look at it and see if it's a good solution (or a better starting point...)

killua99’s picture

@NITEMAN: As we talk in IRC, I'll take a look how to handle it.

@gotheric: We (NITEMAN and I) are planing to make some spring code at setp 18 (may be 17 - 18 ) to rewrite this piece of code. If we can handle the DecodeRule in a good way (like unix for example) I'll ask you for share the mantenice to help you in the future for the rest of the task.

The last "stable" release was in 2009-Oct-01, I think is time to release a 1.3 or 1.5 stable version. ^_^

thanks for the feedback.

~~ [at]killua99

killua99’s picture

Version: 6.x-1.x-dev » 6.x-2.x-dev

The 2.x-dev got the same problem. I got this rule */50 * * * * and it running at 12:50 then 13:00 ... why?

gotheric’s picture

@killua: This bug is already open ;)

However, now elysia cron operates exactly the way you see.
If you use "*/50" in minutes, it runs the job when the remainder of minutes divided by 50 is 0.
So it runs at minute "0" and minute "50" of every hours.

I won't change this soon (maybe never).
Because other use cases are not deterministic:
if i start the server at 12:00 it should run at 12:00, 12:50, 13:40, 14:30...
but if i start it at 13:00 it should run at 13:00, 13:50, 14:40, 15:30...
same rule but different hours.
What if i use a rule like this? "*/50 */3 * * *" or with day of the week? "*/50 * * * */3"
And what if crontab is disabled for some time in the middle?

There is not a single way to intend this pattern.
And to solve it i need to build a more complex timing system.

And i think this is not very useful.
(Maybe you can help changing my idea on this: what is your use case? why you need such a strange timing?)

killua99’s picture

Well 2 things to change your idea.

- first at all you say this "Elysia Cron extends Drupal standard cron, allowing a fine grain control over each task and several ways to add custom cron jobs to your site." and "or use a powerful "linux crontab"-like syntax to set the accurate timings". So if I can't setup an accurate time, remove this line.

- If you support a real "linux crontab style" elysia definitive will be a "most have" module. Because you can control really your jobs.

ok so, my use case, I'm working in idealista.com/news (it/news and pt/news) is a web site of real state news, and for Spain and Italy we have a huge massive visits every single day. I need to control task really accurate. I did a tool for the editors to create a queue of events launcher and they know the audience and they know when every event need to be launch some times they need to be */35 * * * * or */10 * * * * its depends. Thats why I'm looking for this weird times.

So I'm gonna patch your code once more time, because you really don't see the needed to apply this feature.

I'll pass you the patch, and evaluate this.

gotheric’s picture

- first at all you say this "Elysia Cron extends Drupal standard cron, allowing a fine grain control over each task and several ways to add custom cron jobs to your site." and "or use a powerful "linux crontab"-like syntax to set the accurate timings". So if I can't setup an accurate time, remove this line.

You can setup an accurate time.
For example "23 52 1 * *" for me is an accurate time.

- If you support a real "linux crontab style" elysia definitive will be a "most have" module. Because you can control really your jobs.

Linux crontab works like elysia cron do.
Try it.

So I'm gonna patch your code once more time, because you really don't see the needed to apply this feature.

I'm really interested in looking at it!

Developer note: the complete scheduler code in separate from module code. It's in elysia_cron_scheduler.inc
It's easy to create your own scheduler and use it instead of the default one.

And let me see your solution: if it's fast and don't need lot of modifications in persistence tables i can use it as the default one.

kala4ek’s picture

Issue summary: View changes
Status: Active » Closed (outdated)

Drupal 6 is not supported anymore.