-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1b77edd
commit 27a729d
Showing
6 changed files
with
167 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
core/shared/src/main/scala/spice/maintenance/Maintenance.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package spice.maintenance | ||
|
||
import cats.effect.IO | ||
import cats.effect.unsafe.implicits.global | ||
|
||
import java.util.{Calendar, TimeZone} | ||
import scala.concurrent.duration._ | ||
|
||
object Maintenance { | ||
def schedule(name: String, | ||
schedule: => FiniteDuration, | ||
initialDelay: Option[FiniteDuration] = None, | ||
onFail: TaskResult = TaskResult.Continue) | ||
(action: TaskStatus => IO[TaskResult]): MaintenanceTaskInstance = { | ||
var normalSchedule = () => schedule | ||
var stat = TaskStatus().schedule(initialDelay.getOrElse(schedule)) | ||
var cancelled = false | ||
val taskName = name | ||
val task = new MaintenanceTaskInstance { | ||
override def name: String = taskName | ||
|
||
override def status: TaskStatus = stat | ||
|
||
override def cancel(): Unit = cancelled = true | ||
} | ||
|
||
def scheduleNext(resultOption: Option[TaskResult]): IO[TaskResult] = { | ||
stat = stat.copy(lastRun = Some(System.currentTimeMillis()), timesRun = stat.timesRun + 1) | ||
if (cancelled) { | ||
stat = stat.copy(nextRun = None, nextSchedule = None) | ||
IO.pure(TaskResult.Stop) | ||
} else { | ||
val nextRunOption = resultOption match { | ||
case None => Some(initialDelay.getOrElse(schedule)) | ||
case Some(TaskResult.Continue) => Some(normalSchedule()) | ||
case Some(TaskResult.Stop) => None | ||
case Some(TaskResult.RunAgain) => Some(0.seconds) | ||
case Some(TaskResult.ChangeSchedule(delay)) => | ||
normalSchedule = delay | ||
Some(delay()) | ||
case Some(TaskResult.NextSchedule(delay)) => Some(delay) | ||
} | ||
nextRunOption match { | ||
case Some(nextRun) => | ||
stat = stat.schedule(nextRun) | ||
IO.sleep(nextRun).flatMap { _ => | ||
val io = action(stat).handleError { throwable => | ||
scribe.error(s"$name maintenance task failed, will $onFail", throwable) | ||
onFail | ||
} | ||
io.flatMap { result => | ||
scheduleNext(Some(result)) | ||
} | ||
} | ||
case None => | ||
stat = stat.copy(nextRun = None, nextSchedule = None) | ||
IO.pure(TaskResult.Stop) | ||
} | ||
} | ||
} | ||
|
||
scheduleNext(None).unsafeRunAndForget() | ||
|
||
task | ||
} | ||
|
||
def fromTime(hour: Int, | ||
minute: Int = 0, | ||
second: Int = 0, | ||
millisecond: Int = 0, | ||
rollToNextDay: Boolean = true, | ||
timeZone: TimeZone = TimeZone.getDefault): FiniteDuration = { | ||
val c = Calendar.getInstance(timeZone) | ||
c.set(Calendar.HOUR_OF_DAY, hour) | ||
c.set(Calendar.MINUTE, minute) | ||
c.set(Calendar.SECOND, second) | ||
c.set(Calendar.MILLISECOND, millisecond) | ||
val l = c.getTimeInMillis - System.currentTimeMillis() | ||
if (l <= 0 && rollToNextDay) { | ||
(l + 24.hours.toMillis).millis | ||
} else { | ||
l.millis | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
core/shared/src/main/scala/spice/maintenance/MaintenanceTask.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package spice.maintenance | ||
|
||
import cats.effect.IO | ||
|
||
import scala.concurrent.duration.FiniteDuration | ||
|
||
trait MaintenanceTask { | ||
def name: String = getClass.getSimpleName.replace("$", "") | ||
|
||
def nextRun: FiniteDuration | ||
|
||
def initialDelay: FiniteDuration = nextRun | ||
|
||
def onFail: TaskResult = TaskResult.Continue | ||
|
||
def apply(status: TaskStatus): IO[TaskResult] | ||
|
||
def schedule(): MaintenanceTaskInstance = Maintenance.schedule( | ||
name = name, | ||
schedule = nextRun, | ||
initialDelay = Some(initialDelay), | ||
onFail = onFail | ||
)(apply) | ||
} |
9 changes: 9 additions & 0 deletions
9
core/shared/src/main/scala/spice/maintenance/MaintenanceTaskInstance.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package spice.maintenance | ||
|
||
trait MaintenanceTaskInstance { | ||
def name: String | ||
|
||
def status: TaskStatus | ||
|
||
def cancel(): Unit | ||
} |
35 changes: 35 additions & 0 deletions
35
core/shared/src/main/scala/spice/maintenance/TaskResult.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package spice.maintenance | ||
|
||
import scala.concurrent.duration.FiniteDuration | ||
|
||
sealed trait TaskResult | ||
|
||
object TaskResult { | ||
/** | ||
* Should continue on the normal schedule. | ||
*/ | ||
case object Continue extends TaskResult | ||
|
||
/** | ||
* Should stop and not run again. | ||
*/ | ||
case object Stop extends TaskResult | ||
|
||
/** | ||
* Should run again immediately. | ||
*/ | ||
case object RunAgain extends TaskResult | ||
|
||
/** | ||
* Configures a new scheduling delay for the next and all future schedulings. | ||
*/ | ||
case class ChangeSchedule(delay: () => FiniteDuration) extends TaskResult | ||
object ChangeSchedule { | ||
def to(delay: => FiniteDuration): ChangeSchedule = new ChangeSchedule(() => delay) | ||
} | ||
|
||
/** | ||
* Schedules the next run on a new schedule and then will return to the normal schedule. | ||
*/ | ||
case class NextSchedule(delay: FiniteDuration) extends TaskResult | ||
} |
13 changes: 13 additions & 0 deletions
13
core/shared/src/main/scala/spice/maintenance/TaskStatus.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package spice.maintenance | ||
|
||
import scala.concurrent.duration.FiniteDuration | ||
|
||
case class TaskStatus(lastRun: Option[Long] = None, | ||
timesRun: Int = 0, | ||
nextRun: Option[Long] = None, | ||
nextSchedule: Option[FiniteDuration] = None) { | ||
def schedule(duration: FiniteDuration): TaskStatus = copy( | ||
nextRun = Some(System.currentTimeMillis() + duration.toMillis), | ||
nextSchedule = Some(duration) | ||
) | ||
} |