score:7

Accepted answer

Write once, run everyday

val GatherStatisticsPeriod = 24 hours

private[this] val scheduled = new AtomicBoolean(false)

def calcBeforeMidnight: Duration = { 
  // TODO implement 
} 

def preRestart(reason: Throwable, message: Option[Any]) {
  self ! GatherStatisticsScheduled(scheduled.get)
  super.preRestart(reason, message)
}

def schedule(period: Duration, who: ActorRef) = 
  ServerRoot.actorSystem.scheduler
    .scheduleOnce(period)(who ! GatherStatisticsTick)

def receive = {

  case StartServer(nodeName) => 
    sender ! ServerStarted(nodeName)
    if (scheduled.compareAndSet(false, true)) 
      schedule(calcBeforeMidnight, self)

  case GatherStatisticsTick =>
    stats.update
    scheduled.set(true)
    schedule(GatherStatisticsPeriod, self) 

  case GatherStatisticsScheduled(isScheduled) =>
    if (isScheduled && scheduled.compareAndSet(false, isScheduled))
      schedule(calcBeforeMidnight, self)

}

I believe that Akka's scheduler handles restarts internally, one way or another. I used non-persistent way of sending a message to self - actually no strict guarantee of delivery. Also, ticks may vary, so GatherStatisticsPeriod might be a function.

score:1

Just to add another way to achieve it, this can be done using Akka Streams by ticking a message and filtering on time.

Source
  .tick(0.seconds, 2.seconds, "hello") // emits "hello" every two seconds
  .filter(_ => {
    val now = LocalDateTime.now.getSecond
    now > 20 && now < 30 // will let through only if the timing is right.
  })
  .runForeach(n => println("final sink received " + n))

score:6

To use this kind of scheduling in Akka, you would have to roll your own or maybe use Quartz, either through Akka Camel or this prototype quartz for akka.

If you don't need anything fancy and extremely accurate, then I would just calculate the delay to the desired first time and use that as the start delay to the schedule call, and trust the interval.

score:6

Let's say you want to run your task every day at 13 pm.

import scala.concurrent.duration._
import java.time.LocalTime

val interval = 24.hours
val delay = {
  val time = LocalTime.of(13, 0).toSecondOfDay
  val now = LocalTime.now().toSecondOfDay
  val fullDay = 60 * 60 * 24
  val difference = time - now
  if (difference < 0) {
    fullDay + difference
  } else {
    time - now
  }
}.seconds

system.scheduler.schedule(delay, interval)(doSomething())

Also remember that server timezone may be different from yours.


Related Query

More Query from same tag