title: Ref Updates And Fiber Triggers Using Cats Effect
body: <p><strong>Problem:</strong> <em>I am trying to solve a problem where I need to schedule for every x minutes, I need to update the cache and concurrent gets are possible.</em></p>
  <p><strong>Solutions tried:</strong></p>
  <ol>
  <li><strong>Using TrieMap and ScheduledThreadPool Executor With Cats Effects:</strong></li>
  </ol>
  <p>I actually started with using TrieMap as it provides thread safety and used scheduled thread pool for scheduling the update</p>
  <pre class="lang-scala prettyprint-override"><code>import cats.Applicative.ops.toAllApplicativeOps
  import cats.effect.concurrent.Ref
  import cats.effect.{ExitCode, IO, IOApp}
  
  import java.util.concurrent.{Executors, ScheduledExecutorService}
  import scala.collection.concurrent.TrieMap
  import scala.concurrent.duration.{DurationInt, FiniteDuration}
  import scala.util.Random
  
  object ExploreTrieMap extends IOApp {
    def callForEvery[A](f: =&gt; Unit, d: FiniteDuration)
                       (implicit sc: ScheduledExecutorService): IO[Unit] = {
      IO.cancelable {
        cb =&gt;
          val r = new Runnable {
            override def run(): Unit = cb(Right(f))
          }
          val scFut = sc.scheduleAtFixedRate(r, 0, d.length, d.unit)
          IO(scFut.cancel(false)).void
      }
    }
  
    val map = TrieMap.empty[String, String]
    override def run(args: List[String]): IO[ExitCode] = {
      implicit val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
      for {
        _ &lt;- callForEvery(println(map.get(&quot;token&quot;)), 1 second)
        _ &lt;- callForEvery(println(map.put(&quot;token&quot;, Random.nextString(10))), 3 second)
      } yield ExitCode.Success
    }
  }
  
  </code></pre>
  <ol start="2">
  <li><strong>Using Ref and Cats Effect Fibers:</strong></li>
  </ol>
  <p>And then created a pure cats-effect solution.</p>
  <p>Will this below code end up in StackOverflow error?</p>
  <pre class="lang-scala prettyprint-override"><code>import cats.effect.concurrent.Ref
  import cats.effect.{ContextShift, ExitCode, Fiber, IO, IOApp}
  
  import scala.concurrent.Future
  import scala.concurrent.duration.{DurationInt, FiniteDuration}
  import scala.util.Random
  
  object ExploreCatFiber extends IOApp {
    override def run(args: List[String]): IO[ExitCode] = {
      for {
        ref &lt;- Ref.of[IO, String](&quot;&quot;)
        s &lt;- scheduleAndPopulate(ref, 1 minute)
        r &lt;- keepPollingUsingFiber(ref)
        _ &lt;- s.join
        _ &lt;- r.join
      } yield ExitCode.Success
    }
  
    def populate(): Future[String] = Future.successful(Random.nextString(10))
  
    val futPop = IO.fromFuture(IO(populate()))
  
    def scheduleAndPopulate(r: Ref[IO, String], duration: FiniteDuration)(implicit cs: ContextShift[IO]): IO[Fiber[IO, Unit]] = {
      (for {
        _ &lt;- IO(println(&quot;Scheduled For Populating Ref&quot;))
        res &lt;- futPop
        _ &lt;- r.set(res)
        _ &lt;- IO.sleep(duration)
        rS &lt;- scheduleAndPopulate(r, duration)(cs)
        _ &lt;- rS.join
      } yield ()).start(cs)
    }
  
  
    def keepPollingUsingFiber(r: Ref[IO, String])(implicit cs: ContextShift[IO]): IO[Fiber[IO, Unit]] = {
      (for {
        res &lt;- r.get
        _ &lt;- IO(println(res))
        _ &lt;- IO.sleep(1 second)
        w &lt;- keepPollingUsingFiber(r)(cs)
        _ &lt;- w.join
      } yield ()).start(cs)
    }
  }
  
  </code></pre>
  <p>I'm trying to update a Ref, and use the Ref like a concurrent cache that is being updated by another fiber. And I'm triggering the fiber creation using recursion. I know fibers can be used for stacksafe operations. In this case, I'm joining on the old fiber created. So wanted to understand is the below code safe.</p>
  <h1>Update (Solution from answers provided below)</h1>
  <p>Third solution: Based on input from one of the answers. Rather than forking for each recursive call, fork it on the caller.</p>
  <pre class="lang-scala prettyprint-override"><code>import cats.effect.concurrent.Ref
  import cats.effect.{ContextShift, ExitCode, Fiber, IO, IOApp}
  
  import scala.concurrent.Future
  import scala.concurrent.duration.{DurationInt, FiniteDuration}
  import scala.util.Random
  
  object ExploreCatFiberWithIO extends IOApp {
    override def run(args: List[String]): IO[ExitCode] = {
      for {
        ref &lt;- Ref.of[IO, String](&quot;&quot;)
        s &lt;- scheduleAndPopulateWithIO(ref, 1 second).start
        r &lt;- keepPollingUsingIO(ref).start
        _ &lt;- s.join
        _ &lt;- r.join
      } yield ExitCode.Success
    }
  
    def populate(): Future[String] = Future.successful(Random.nextString(10))
  
    val futPop = IO.fromFuture(IO(populate()))
  
    def scheduleAndPopulateWithIO(r: Ref[IO, String], duration: FiniteDuration)(implicit cs: ContextShift[IO]): IO[Unit] = {
      for {
        _ &lt;- IO(println(&quot;Scheduled For Populating Ref&quot;))
        res &lt;- futPop
        _ &lt;- r.set(res)
        _ &lt;- IO.sleep(duration)
        _ &lt;- scheduleAndPopulateWithIO(r, duration)(cs)
      } yield ()
    }
  
    def keepPollingUsingIO(r: Ref[IO, String])(implicit cs: ContextShift[IO]): IO[Unit] = {
      (for {
        res &lt;- r.get
        _ &lt;- IO(println(res))
        _ &lt;- IO.sleep(1 second)
        w &lt;- keepPollingUsingIO(r)(cs)
      } yield ())
    }
  }
  
  </code></pre>
  <p><em><strong>Would love to know the pros and cons of the approaches discussed above.</strong></em></p>