scalazio

In ZIO how to make a Schedule which repeats tries while multiple conditions are met counting tries for each condition individually


I am using ZIO and my task is to retry request to some service with such logic (simplified):

  1. If response contains some incorrect data - make N retries, if situation have not changed - keep result
  2. If response contains not enough data - make M retries, if situation have not changed - keep result

I am planning to use .repeat function from ZIO, but can not figure out how to write schedule which will count retries individually for each condition.

package com.mycompany

import zio.Schedule.WithState
import zio.{ExitCode, Schedule, Task, URIO, ZIO, ZIOAppDefault}

import scala.util.Random

object Main3 extends ZIOAppDefault {
  def makeRequest: Task[Float] = ZIO.attempt {
    new Random().nextFloat() * 10
  }

  def makeSchedule(): WithState[(Unit, Unit), Any, Float, Float] = {
    val firstCondition =
      Schedule.recurUntil[Float](float => float > 3)
        // TODO: make only 3 tries for this condition
        .onDecision {
          case (input, value, decision) => ZIO.log(s"FirstCondition: ${input}, ${value}, ${decision}")
        }


    val secondCondition =
      Schedule.recurUntil[Float](float => float < 9)
        // TODO: make only 5 tries for this condition
        .onDecision {
          case (input, value, decision) => ZIO.log(s"SecondCondition: ${input}, ${value}, ${decision}")
        }

    firstCondition.||(secondCondition).map(_._1)
  }

  override def run: URIO[Any, ExitCode] = {
    makeRequest
      .repeat(makeSchedule())
      .tap(res => ZIO.log(f"RESULT IS: $res"))
      .exitCode
  }
}

Solution

  • I found the solution:

    .repetitions
    .whileOutput(repetition => repetition < 3)
    .passthrough[Float]
    

    is doing the job. Calling passthrough[Float] allows to align types and get result from repeat. Final code snippet is

    package com.mycompany
    
    import zio.Schedule.WithState
    import zio.{ExitCode, Schedule, Task, URIO, ZIO, ZIOAppDefault}
    
    import scala.util.Random
    
    object Main3 extends ZIOAppDefault {
     def makeRequest: Task[Float] = ZIO.attempt {
       new Random().nextFloat() * 10
     }
    
     def makeSchedule() = {
       val firstCondition =
         Schedule.recurUntil[Float](float => float > 8)
           .repetitions
           .whileOutput(repetition => repetition < 3)
           .passthrough[Float]
           .onDecision {
             case (input, value, decision) => ZIO.log(s"FirstCondition: ${input}, ${value}, ${decision}")
           }
    
    
       val secondCondition: WithState[(Unit, Long), Any, Float, Float] =
         Schedule.recurUntil[Float](float => float < 9)
           .repetitions
           .whileOutput(repetition => repetition < 5)
           .passthrough[Float]
           .onDecision {
             case (input, value, decision) => ZIO.log(s"SecondCondition: ${input}, ${value}, ${decision}")
           }
    
       firstCondition.||(secondCondition).map(_._1)
     }
    
     override def run: URIO[Any, ExitCode] = {
       makeRequest
         .repeat(makeSchedule())
         .tap(res => ZIO.log(f"RESULT IS: $res"))
         .exitCode
     }
    }