views:

52

answers:

2

I have a timer thread that needs to run at a particular moments of the day to do an incremental replication with a database. Right now it runs at the hour, 15 minutes past the hour, 30 minutes past the hour and 45 minutes past the hour. This is the code I have which is working ok:

public class TimerRunner implements Runnable {

    private static final Semaphore lock = new Semaphore(1);

    private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public static void initialize() {
        long delay = getDelay();
        executor.schedule(new TimerRunner(), delay, TimeUnit.SECONDS);
    }

    public static void destroy() {
        executor.shutdownNow();
    }

    private static long getDelay() {
        Calendar now = Calendar.getInstance();
        long p = 15 * 60; // run at 00, 15, 30 and 45 minutes past the hour
        long second = now.get(Calendar.MINUTE) * 60 + now.get(Calendar.SECOND);
        return p - (second % p);
    }

    public static void replicate() {
        if (lock.tryAcquire()) {
            try {
                Thread t = new Thread(new Runnable() {
                    public void run() {
                        try {
                            // here is where the magic happens
                        } finally {
                            lock.release();
                        }
                    }
                });
                t.start();
            } catch (Exception e) {
                lock.release();
            }
        } else {
            throw new IllegalStateException("already running a replicator");
        }
    }

    public void run() {
        try {
            TimerRunner.replicate();
        } finally {
            long delay = getDelay();
            executor.schedule(new TimerRunner(), delay, TimeUnit.SECONDS);
        }
    }

}

This process is started by calling TimerRunner.initialize() when a server starts and calling TimerRunner.destroy().

I have created a full replication process (as opposed to incremental) that I would like to run at a certain moment of the day, say 2:00am. How would change the above code to do this? I think that it should be very simple something like if it is now around 2:00am and it's been a long time since I did the full replication then do it now, but I can't get the if right.

Beware that sometimes the replicate process takes way longer to complete. Sometimes beyond the 15 minutes, posing a problem in running at around 2:00am.

+4  A: 

IMHO, if it does something different, it shouldn't be the same runnable. Create some generic abstract class and two subclasses, each with the specific behavior. Then have your scheduling logic schedule the appropriate thread at the appropriate time.

Otherwise, you have an object that has two responsibilities and needs to know about scheduling.

I would personally also get the scheduling logic out of your current getDelay() and into some replication scheduling manager object that would also maintain state (e.g., what was recently executed).

Finally, you may want to use library stuff for your schedulers, it feels to me like you're reinventing some wheels.

Uri
+3  A: 

Don't build your own scheduler, use existing one.

If you want to do this in Java use quartz and schedule 2 separate tasks for incremental and full backups that run on different schedules.

But in your case a simple cron script may be just fine. If you are running on Windows, search for Scheduled Tasks in your Windows Help file.

EDIT
Here is the crontab that avoids running both full and incremental at the same time:

*/15 0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 * * * /path/to/run-incremental-backup.sh
15,30,45 1 * * * /path/to/run-incremental-backup.sh
0 1 * * * /path/to/run-full-backup.sh

In you shell scripts setup your java environment and run java programs responsible for the tasks.

Quartz schedules can be very fine tuned, one of the standard schedulers completely mimics crontab. I leave this as an exercise for the reader.

BTW, I've scheduled your full backup to run at 1 AM, see below why.

NOTE ON Daylight Savings Time
I don't know where in the world you are, if you are one of the few lucky ones you don't have the Daylight Savings Time, otherwise check with your local time zone authorities. Here in North America Standard To Daylight happens at 2:00 AM and Daylight to Standard at 3:00 AM, so if your full backup is scheduled for 2:00 AM, then one day a year ( S to D ) your full backup will not run, and another day ( D to S ) it would run twice.

Alexander Pogrebnyak
One problem with the two schedulers is that at 2:00am I would have to run the incremental and the full but I only need the full. How to check for race conditions between the two scheduled process?
rmarimon
@rmarimon. I've updated my answer to address your comments.
Alexander Pogrebnyak