Does anyone have idea about writing a cron job/ sc...
# server
a
Does anyone have idea about writing a cron job/ schedule job in Ktor..I want to do some clean up after every 24 hours or hit a remote API and fetch some data and store it in database ?
d
you can use http://www.quartz-scheduler.org/ or https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html , those are not integrated in ktor though, you’d need to write the code to use any of them
👍 3
p
If you're on JVM, you can use the ancient/battle-tested cron4j library to abstract things out. I wrote a very simple abstraction in case I wanted to change out the backend at some point
👍 2
a
whats ur suggestions on writing something like launch { while(true) delay(6000) //task } should this work ??
p
that should certainly work, but is basically the equivalent of using a scheduled executor service
👍 1
a
Wouldn't that drift and make it start later and later?
j
We wrote a number of very basic schedulers using
delay
, such as
Copy code
fun CoroutineScope.runEvery(
    duration: Duration,
    initialDelay: Duration = Duration.ZERO,
    action: suspend () -> Unit
) = launch(start = CoroutineStart.UNDISPATCHED) {
    delay(initialDelay.toMillis())
    while (true) {
        try {
            action()
        } catch (e: Exception) {
            logger.error(e) { "Task repeated every $duration threw exception" }
        }
        delay(duration.toMillis())
    }
}
Like @Alex Nordlund says, this does not correct for drift, so we only use it for cases where we want to run stuff say roughly every hour. To run stuff daily at or just after a certain time, we use something like
Copy code
fun CoroutineScope.runDailyAt(runAt: LocalTime, action: suspend () -> Unit) =
    launch(start = CoroutineStart.UNDISPATCHED) {
        while (true) {
            val now = TimeService.now()
            val planned = runAt.atDate(now.toLocalDate()).atZone(now.zone).let { planned ->
                if (now.isBefore(planned)) planned else planned.plusDays(1)
            }
            logger.debug { "Scheduling next daily task at $planned" }

            delay(Duration.between(now, planned).toMillis())
            try {
                action()
            } catch (e: Exception) {
                logger.error(e) { "Daily task threw exception" }
            }
        }
    }
And we have a few versions parsing cron schedules etcetera but the basic idea is the same. It's simple but so far it works really well.
👍 2
a
(In a lot of cases the drift isn't actually a problem, if you just want a cleanup then the drift is probably fine, if you want to do periodic updaters it's probably also fine, but if you need to do updates before the day starts this is eventually going to become a problem)
👍 3
r
Personally been happy with write a console app and then use k8s jobs to handle the execution: https://kubernetes.io/docs/concepts/workloads/controllers/job/