Skip to content

Commit

Permalink
added retryRaise and retryEither functions
Browse files Browse the repository at this point in the history
  • Loading branch information
akotynski committed Feb 11, 2024
1 parent d42429f commit afe431d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@file:OptIn(ExperimentalTypeInference::class)

import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.either
import arrow.resilience.Schedule
import arrow.resilience.ScheduleStep
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlin.experimental.ExperimentalTypeInference
import kotlin.time.Duration

public suspend inline fun <Error, Result, Output> Schedule<Error, Output>.retryRaise(
@BuilderInference action: Raise<Error>.() -> Result,
): Either<Error, Result> = retryEither { either(action) }

public suspend inline fun <Error, Result, Output> Schedule<Error, Output>.retryEither(
@BuilderInference action: () -> Either<Error, Result>,
): Either<Error, Result> {
var step: ScheduleStep<Error, Output> = step

while (true) {
currentCoroutineContext().ensureActive()
when (val result = action()) {
is Either.Left -> when (val decision = step(result.value)) {
is Schedule.Decision.Continue -> {
if (decision.delay != Duration.ZERO) delay(decision.delay)
step = decision.step
}

is Schedule.Decision.Done -> return result
}

is Either.Right -> return result
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package arrow.resilience

import arrow.atomic.AtomicLong
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.runTest
import retryEither
import retryRaise
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class ScheduleEitherTest {

@Test
fun retryRaiseIsStackSafe(): TestResult = runTest {
val count = AtomicLong(0)
val iterations = 20_000L

suspend fun increment() {
count.incrementAndGet()
}

val result = Schedule.recurs<CustomError>(iterations).retryRaise {
increment()
raise(CustomError)
}

assertTrue { result is Either.Left }
assertEquals(iterations + 1, count.get())
}

@Test
fun retryRaiseSucceedsIfErrorIsNotRaised(): TestResult = runTest {
val result = Schedule.recurs<CustomError>(0).retryRaise { 1 }

assertTrue { result is Either.Right && result.value == 1 }
}

@Test
fun retryEitherIsStackSafe(): TestResult = runTest {
val count = AtomicLong(0)
val iterations = 20_000L

suspend fun increment() {
count.incrementAndGet()
}

val result = Schedule.recurs<CustomError>(iterations).retryEither {
increment()
CustomError.left()
}

assertTrue { result is Either.Left }
assertEquals(iterations + 1, count.get())
}

@Test
fun retryEitherSucceedsIfErrorIsNotRaised(): TestResult = runTest {
val result = Schedule.recurs<CustomError>(0).retryEither { 1.right() }

assertTrue { result is Either.Right && result.value == 1 }
}
}

private object CustomError

0 comments on commit afe431d

Please sign in to comment.