Request for comments: cron jobs and outputs

#1

I’d like to get some feedback on this:

Let’s say I decide to use a cron job to control some output. I will try to avoid setting several cron jobs for a single time frame.
That output has to be on from 3:20 to 4:10 every day of the week (and not on weekends), so I set my cron jobs with one entry for the on time and then at the callback handler I fire a timer for the off time. Outputs are off at system start.

Now, the mains power goes off at 3:21 and comes back at 3:22, my output does not come back on.
So, I write some code to keep outputs persistent, and insert an extra cron job to set the output off.
Now one day the mains power goes off at 3:19 and comes back at 3:21, and my output never goes on.
The next day it goes off at 4:09 and comes back at 4:11, and my output never goes off, what is even worse…

So I go for the @random feature, and choose 50 invocations, and I remove the persistency of the outputs, so the system starts with all outputs off again.
Now, if the mains power goes off either before or during the output on time, I’m not guaranteed to get a call to turn the output on, but since 50 random points are uniformly distributed over a 50 minute period it is likely to be called every minute, but there is no guarantee.
If the mains does not interrupt, ~50 calls take place, so the handler has to be idempotent (though turning a gpio on usually is).
I can trade off number of calls against probability of not being called if very close to the off time; the penalty for a big number of random points (besides the number of calls) is the evaluation frequency, the next point is evaluated every time the former point is reached, or time changes, so that is done every x = (to - from)/points units of time
But how do I turn the output off ? Well, every call could (re)start a timer, whose timeout will turn the output off. But the time between calls is… random… so there could be some glitches.
If time somehow changes while the output was on, and now it is supposed to be off, it will happen when the timer expires (or caught by a time change callback handler with some extra work).
So far the @random method is mostly good, but there are still some nuances.

An improvement could be to turn the random into deterministic, remove the random points and generate a certain number of calls using the */n approach. Let’s name it @range. This requires a structure very similar to the @random structure, mostly code reuse and minor modifications.

A “hard way”, a baremetal approach, would be to not use cron jobs and instead, every x time units, check the activation time interval against the current time; even do it at the main loop.
Every check action is in itself very similar to evaluating the random points distribution against current time at a time change event, so the same function could be reused with minor modifications
The overhead depends on the amount of time between evaluations
The user handles the time between evaluations, there are usual tricks of the trade to reduce the overhead, and in reality there is a limited number of outputs requiring this processing to even consider scaling issues.
The process is usually stateful, no glitches, just some overkill perhaps.

Another option might make use of a complementary functionality.
At time change:

  • if current time is within the activation range (from < time < to)
    • call the “activate” callback registered handler
    • schedule a job to call the “deactivate” callback registered handler
  • if it is outside that range
    • call the “deactivate” callback registered handler

This also requires a structure very similar to the @random structure, but with two callback handlers; let’s name it @onoff, mostly code reuse and minor modifications.
Idempotency is still required on both functions, which usually is no problem. Otherwise overhead is minimal.
Providing there are no “missed events”, this seems to be optimal.

What are your thoughts on this, simpler solutions ? Problems I did not foresee ? Thanks.