Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to schedule a periodic task that is immune to system time change using Python

I am using python's sched module to run a task periodically, and I think I have come across a bug.

I find that it relies on the time of the system on which the python script is run. For example, let's say that I want to run a task every 5 seconds. If I forward the system time, the scheduled task will run as expected. However, if I rewind the system time to, say 1 day, then the next scheduled task will run in 5 seconds + 1 day.

If you run the script below and then change your system time by a few days back, then you can reproduce the issue. The problem can be reproduced on Linux and Windows.

import sched
import time
import threading

period = 5
scheduler = sched.scheduler(time.time, time.sleep)

def check_scheduler():
    print time.time()
    scheduler.enter(period, 1, check_scheduler, ())

if __name__ == '__main__':
    print time.time()

    scheduler.enter(period, 1, check_scheduler, ())

    thread = threading.Thread(target=scheduler.run)
    thread.start()
    thread.join()
    exit(0)

Anyone has any python solution around this problem?

like image 752
Vincent Avatar asked Oct 19 '22 11:10

Vincent


1 Answers

From the sched documentation:

class sched.scheduler(timefunc, delayfunc)

The scheduler class defines a generic interface to scheduling events. It needs two functions to actually deal with the “outside world” — timefunc should be callable without arguments, and return a number (the “time”, in any units whatsoever). The delayfunc function should be callable with one argument, compatible with the output of timefunc, and should delay that many time units. delayfunc will also be called with the argument 0 after each event is run to allow other threads an opportunity to run in multi-threaded applications.

The problem you have is that your code uses time.time() as timefunc, whose return value (when called without arguments) is the current system time and is thus affected by re-winding the system clock.

To make your code immune to system time changes you'd need to provide a timefunc which doesn't depend on the system time, start/current timestamps, etc.

You can write your own function, for example one returning the number of seconds since your process is started, which you'd have to actually count in your code (i.e. don't compute it based on timestamp deltas). The time.clock() function might help, if it's based on CPU time counters, but I'm not sure if that's true or not.

like image 82
Dan Cornilescu Avatar answered Nov 01 '22 14:11

Dan Cornilescu