Repeat

The repeat functions allow to repeat an operation according to a given schedule (e.g. repeat 3 times with a 100ms interval and 50ms of initial delay).

API

The basic syntax for repeat is:

import ox.scheduling.repeat

repeat(config)(operation)

The repeat API uses scheduled underneath with DSL focused on repeats. See scheduled for more details.

Operation definition

Similarly to the retry API, the operation can be defined:

  • directly using a by-name parameter, i.e. f: => T

  • using a by-name Either[E, T]

  • or using an arbitrary error mode, accepting the computation in an F context: f: => F[T].

Configuration

The repeat config requires a Schedule, which indicates how many times and with what interval should the operation be repeated.

In addition, it is possible to define a custom shouldContinueOnSuccess strategy for deciding if the operation should continue to be repeated after a successful result returned by the previous operation (defaults to _: T => true).

If an operation returns an error, the repeat loop will always be stopped. If an error handling within the operation is needed, you can use a retry inside it (see an example below) or use scheduled instead of repeat, which allows full customization.

API shorthands

You can use one of the following shorthands to define a RepeatConfig with a given schedule with an optional initial delay:

  • RepeatConfig.immediate(maxInvocations: Int, initialDelay: Option[FiniteDuration] = None)

  • RepeatConfig.immediateForever[E, T](initialDelay: Option[FiniteDuration] = None)

  • RepeatConfig.fixedRate[E, T](maxInvocations: Int, interval: FiniteDuration, initialDelay: Option[FiniteDuration] = None)

  • RepeatConfig.fixedRateForever[E, T](interval: FiniteDuration, initialDelay: Option[FiniteDuration] = None)

  • RepeatConfig.backoff[E, T](maxInvocations: Int, firstInterval: FiniteDuration, maxInterval: FiniteDuration = 1.minute, jitter: Jitter = Jitter.None, initialDelay: Option[FiniteDuration] = None)

  • RepeatConfig.backoffForever[E, T](firstInterval: FiniteDuration, maxInterval: FiniteDuration = 1.minute, jitter: Jitter = Jitter.None, initialDelay: Option[FiniteDuration] = None)

See scheduled for details on how to create custom schedules.

Examples

import ox.UnionMode
import ox.scheduling.{Jitter, Schedule, repeat, repeatEither, repeatWithErrorMode, RepeatConfig}
import ox.resilience.{retry, RetryConfig}
import scala.concurrent.duration.*

def directOperation: Int = ???
def eitherOperation: Either[String, Int] = ???
def unionOperation: String | Int = ???

// various operation definitions - same syntax
repeat(RepeatConfig.immediate(3))(directOperation)
repeatEither(RepeatConfig.immediate(3))(eitherOperation)

// various schedules
repeat(RepeatConfig.fixedRate(3, 100.millis))(directOperation)
repeat(RepeatConfig.fixedRate(3, 100.millis, Some(50.millis)))(directOperation)

// infinite repeats with a custom strategy
def customStopStrategy: Int => Boolean = ???
repeat(RepeatConfig.fixedRateForever(100.millis).copy(shouldContinueOnResult = customStopStrategy))(directOperation)

// custom error mode
repeatWithErrorMode(UnionMode[String])(RepeatConfig.fixedRate(3, 100.millis))(unionOperation)

// repeat with retry inside
repeat(RepeatConfig.fixedRate(3, 100.millis)) {
  retry(RetryConfig.backoff(3, 100.millis))(directOperation)
}